Mercurial > thymian
comparison 3rdparty/vmime/doc/book/msg.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{Parsing and Building Messages} | |
| 2 | |
| 3 % ============================================================================ | |
| 4 \section{Parsing messages} | |
| 5 | |
| 6 \subsection{Introduction} % -------------------------------------------------- | |
| 7 | |
| 8 Parsing is the process of creating a structured representation (for example, | |
| 9 a hierarchy of C++ objects) of a message from its ``textual'' representation | |
| 10 (the raw data that is actually sent on the Internet). | |
| 11 | |
| 12 For example, say you have the following email in a file called "hello.eml": | |
| 13 | |
| 14 \begin{verbatim} | |
| 15 Date: Thu, Oct 13 2005 15:22:46 +0200 | |
| 16 From: Vincent <vincent@vmime.org> | |
| 17 To: you@vmime.org | |
| 18 Subject: Hello from VMime! | |
| 19 | |
| 20 A simple message to test VMime | |
| 21 \end{verbatim} | |
| 22 | |
| 23 The following code snippet shows how you can easily obtain a | |
| 24 {\vcode vmime::message} object from data in this file: | |
| 25 | |
| 26 \begin{lstlisting}[caption={Parsing a message from a file}] | |
| 27 // Read data from file | |
| 28 std::ifstream file; | |
| 29 file.open("hello.eml", std::ios::in | std::ios::binary); | |
| 30 | |
| 31 vmime::utility::inputStreamAdapter is(file); | |
| 32 | |
| 33 vmime::string data; | |
| 34 vmime::utility::outputStreamStringAdapter os(data); | |
| 35 | |
| 36 vmime::utility::bufferedStreamCopy(is, os); | |
| 37 | |
| 38 // Actually parse the message | |
| 39 vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); | |
| 40 msg->parse(data); | |
| 41 | |
| 42 vmime::shared_ptr <vmime::header> hdr = msg->getHeader(); | |
| 43 vmime::shared_ptr <vmime::body> bdy = msg->getBody(); | |
| 44 | |
| 45 // Now, you can extract some of its components | |
| 46 vmime::charset ch(vmime::charsets::UTF_8); | |
| 47 | |
| 48 std::cout | |
| 49 << "The subject of the message is: " | |
| 50 << hdr->Subject()->getValue <vmime::text>()->getConvertedText(ch) | |
| 51 << std::endl | |
| 52 << "It was sent by: " | |
| 53 << hdr->From()->getValue <vmime::mailbox>()->getName().getConvertedText(ch) | |
| 54 << " (email: " << hdr->From()->getValue <vmime::mailbox>()->getEmail() << ")" | |
| 55 << std::endl; | |
| 56 \end{lstlisting} | |
| 57 | |
| 58 The output of this program is: | |
| 59 | |
| 60 \begin{verbatim} | |
| 61 The subject of the message is: Hello from VMime! | |
| 62 It was sent by: Vincent (email: vincent@vmime.org) | |
| 63 \end{verbatim} | |
| 64 | |
| 65 | |
| 66 \subsection{Using the {\vcode vmime::messageParser} object} % ---------------- | |
| 67 | |
| 68 The {\vcode vmime::messageParser} object allows to parse messages in a more | |
| 69 simple manner. You can obtain all the text parts and attachments as well as | |
| 70 basic fields (expeditor, recipients, subject...), without dealing with | |
| 71 MIME message structure. | |
| 72 | |
| 73 \begin{lstlisting}[caption={Using {\vcode vmime::messageParser} to parse | |
| 74 more complex messages}] | |
| 75 // Read data from file | |
| 76 std::ifstream file; | |
| 77 file.open("hello.eml", std::ios::in | std::ios::binary); | |
| 78 | |
| 79 vmime::utility::inputStreamAdapter is(file); | |
| 80 | |
| 81 vmime::string data; | |
| 82 vmime::utility::outputStreamStringAdapter os(data); | |
| 83 | |
| 84 vmime::utility::bufferedStreamCopy(is, os); | |
| 85 | |
| 86 // Actually parse the message | |
| 87 vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); | |
| 88 msg->parse(data); | |
| 89 | |
| 90 // Here start the differences with the previous example | |
| 91 vmime::messageParser mp(msg); | |
| 92 | |
| 93 // Output information about attachments | |
| 94 std::cout << "Message has " << mp.getAttachmentCount() | |
| 95 << " attachment(s)" << std::endl; | |
| 96 | |
| 97 for (int i = 0 ; i < mp.getAttachmentCount() ; ++i) | |
| 98 { | |
| 99 vmime::shared_ptr <const vmime::attachment> att = mp.getAttachmentAt(i); | |
| 100 std::cout << " - " << att->getType().generate() << std::endl; | |
| 101 } | |
| 102 | |
| 103 // Output information about text parts | |
| 104 std::cout << "Message has " << mp.getTextPartCount() | |
| 105 << " text part(s)" << std::endl; | |
| 106 | |
| 107 for (int i = 0 ; i < mp.getTextPartCount() ; ++i) | |
| 108 { | |
| 109 vmime::shared_ptr <const vmime::textPart> tp = mp.getTextPartAt(i); | |
| 110 | |
| 111 // text/html | |
| 112 if (tp->getType().getSubType() == vmime::mediaTypes::TEXT_HTML) | |
| 113 { | |
| 114 vmime::shared_ptr <const vmime::htmlTextPart> htp = | |
| 115 vmime::dynamicCast <const vmime::htmlTextPart>(tp); | |
| 116 | |
| 117 // HTML text is in tp->getText() | |
| 118 // Plain text is in tp->getPlainText() | |
| 119 | |
| 120 // Enumerate embedded objects | |
| 121 for (int j = 0 ; j < htp->getObjectCount() ; ++j) | |
| 122 { | |
| 123 vmime::shared_ptr <const vmime::htmlTextPart::embeddedObject> obj = | |
| 124 htp->getObjectAt(j); | |
| 125 | |
| 126 // Identifier (Content-Id or Content-Location) is obj->getId() | |
| 127 // Object data is in obj->getData() | |
| 128 } | |
| 129 } | |
| 130 // text/plain or anything else | |
| 131 else | |
| 132 { | |
| 133 // Text is in tp->getText() | |
| 134 } | |
| 135 } | |
| 136 \end{lstlisting} | |
| 137 | |
| 138 | |
| 139 % ============================================================================ | |
| 140 \section{Building messages} | |
| 141 | |
| 142 \subsection{A simple message\label{msg-building-simple-message}} % ----------- | |
| 143 | |
| 144 Of course, you can build a MIME message from scratch by creating the various | |
| 145 objects that compose it (parts, fields, etc.). The following is an example of | |
| 146 how to achieve it: | |
| 147 | |
| 148 \begin{lstlisting}[caption={Building a simple message from scratch}] | |
| 149 vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); | |
| 150 | |
| 151 vmime::shared_ptr <vmime::header> hdr = msg->getHeader(); | |
| 152 vmime::shared_ptr <vmime::body> bdy = msg->getBody(); | |
| 153 | |
| 154 vmime::shared_ptr <vmime::headerFieldFactory> hfFactory = | |
| 155 vmime::headerFieldFactory::getInstance(); | |
| 156 | |
| 157 // Append a 'Date:' field | |
| 158 vmime::shared_ptr <vmime::headerField> dateField = | |
| 159 hfFactory->create(vmime::fields::DATE); | |
| 160 | |
| 161 dateField->setValue(vmime::datetime::now()); | |
| 162 hdr->appendField(dateField); | |
| 163 | |
| 164 // Append a 'Subject:' field | |
| 165 vmime::shared_ptr <vmime::headerField> subjectField = | |
| 166 hfFactory->create(vmime::fields::SUBJECT); | |
| 167 | |
| 168 subjectField->setValue(vmime::text("Message subject")); | |
| 169 hdr->appendField(subjectField); | |
| 170 | |
| 171 // Append a 'From:' field | |
| 172 vmime::shared_ptr <vmime::headerField> fromField = | |
| 173 hfFactory->create(vmime::fields::FROM); | |
| 174 | |
| 175 fromField->setValue | |
| 176 (vmime::make_shared <vmime::mailbox>("me@vmime.org")); | |
| 177 hdr->appendField(fromField); | |
| 178 | |
| 179 // Append a 'To:' field | |
| 180 vmime::shared_ptr <vmime::headerField> toField = | |
| 181 hfFactory->create(vmime::fields::TO); | |
| 182 | |
| 183 vmime::shared_ptr <vmime::mailboxList> recipients = | |
| 184 vmime::make_shared <vmime::mailboxList>(); | |
| 185 | |
| 186 recipients->appendMailbox | |
| 187 (vmime::make_shared <vmime::mailbox>("you@vmime.org")); | |
| 188 | |
| 189 toField->setValue(recipients); | |
| 190 hdr->appendField(toField); | |
| 191 | |
| 192 // Set the body contents | |
| 193 bdy->setContents(vmime::make_shared <vmime::stringContentHandler> | |
| 194 ("This is the text of your message...")); | |
| 195 | |
| 196 // Output raw message data to standard output | |
| 197 vmime::utility::outputStreamAdapter out(std::cout); | |
| 198 msg->generate(out); | |
| 199 \end{lstlisting} | |
| 200 | |
| 201 As you can see, this is a little fastidious. Hopefully, VMime also offers a | |
| 202 more simple way for creating messages. The {\vcode vmime::messageBuilder} | |
| 203 object can create basic messages that you can then customize. | |
| 204 | |
| 205 The following code can be used to build exactly the same message as in the | |
| 206 previous example, using the {\vcode vmime::messageBuilder} object: | |
| 207 | |
| 208 \begin{lstlisting}[caption={Building a simple message | |
| 209 using {\vcode vmime::messageBuilder}}] | |
| 210 try | |
| 211 { | |
| 212 vmime::messageBuilder mb; | |
| 213 | |
| 214 // Fill in some header fields and message body | |
| 215 mb.setSubject(vmime::text("Message subject")); | |
| 216 mb.setExpeditor(vmime::mailbox("me@vmime.org")); | |
| 217 mb.getRecipients().appendAddress | |
| 218 (vmime::make_shared <vmime::mailbox>("you@vmime.org")); | |
| 219 | |
| 220 mb.getTextPart()->setCharset(vmime::charsets::ISO8859_15); | |
| 221 mb.getTextPart()->setText(vmime::make_shared <vmime::stringContentHandler> | |
| 222 ("This is the text of your message...")); | |
| 223 | |
| 224 // Message construction | |
| 225 vmime::shared_ptr <vmime::message> msg = mb.construct(); | |
| 226 | |
| 227 // Output raw message data to standard output | |
| 228 vmime::utility::outputStreamAdapter out(std::cout); | |
| 229 msg->generate(out); | |
| 230 } | |
| 231 // VMime exception | |
| 232 catch (vmime::exception& e) | |
| 233 { | |
| 234 std::cerr << "vmime::exception: " << e.what() << std::endl; | |
| 235 } | |
| 236 // Standard exception | |
| 237 catch (std::exception& e) | |
| 238 { | |
| 239 std::cerr << "std::exception: " << e.what() << std::endl; | |
| 240 } | |
| 241 \end{lstlisting} | |
| 242 | |
| 243 | |
| 244 \subsection{Adding an attachment} % ------------------------------------------ | |
| 245 | |
| 246 Dealing with attachments is quite simple. Add the following code to the | |
| 247 previous example to attach a file to the message: | |
| 248 | |
| 249 \begin{lstlisting}[caption={Building a message with an attachment using | |
| 250 {\vcode vmime::messageBuilder}}] | |
| 251 // Create an attachment | |
| 252 vmime::shared_ptr <vmime::fileAttachment> att = | |
| 253 vmime::make_shared <vmime::fileAttachment> | |
| 254 ( | |
| 255 /* full path to file */ "/home/vincent/paris.jpg", | |
| 256 /* content type */ vmime::mediaType("image/jpeg), | |
| 257 /* description */ vmime::text("My holidays in Paris") | |
| 258 ); | |
| 259 | |
| 260 // You can also set some infos about the file | |
| 261 att->getFileInfo().setFilename("paris.jpg"); | |
| 262 att->getFileInfo().setCreationDate | |
| 263 (vmime::datetime("30 Apr 2003 14:30:00 +0200")); | |
| 264 | |
| 265 // Add this attachment to the message | |
| 266 mb.appendAttachment(att); | |
| 267 \end{lstlisting} | |
| 268 | |
| 269 | |
| 270 \subsection{HTML messages and embedded objects} % ---------------------------- | |
| 271 | |
| 272 VMime also supports aggregate messages, which permits to build MIME messages | |
| 273 containing HTML text and embedded objects (such as images). For more information | |
| 274 about aggregate messages, please read RFC-2557 (\emph{MIME Encapsulation of | |
| 275 Aggregate Documents, such as HTML}). | |
| 276 | |
| 277 Creating such messages is quite easy, using the {\vcode vmime::messageBuilder} | |
| 278 object. The following code constructs a message containing text in both plain | |
| 279 and HTML format, and a JPEG image: | |
| 280 | |
| 281 \begin{lstlisting}[caption={Building an HTML message with an embedded image | |
| 282 using the {\vcode vmime::messageBuilder}}] | |
| 283 // Fill in some header fields | |
| 284 mb.setSubject(vmime::text("An HTML message")); | |
| 285 mb.setExpeditor(vmime::mailbox("me@vmime.org")); | |
| 286 mb.getRecipients().appendAddress | |
| 287 (vmime::make_shared <vmime::mailbox>("you@vmime.org")); | |
| 288 | |
| 289 // Set the content-type to "text/html": a text part factory must be | |
| 290 // available for the type you are using. The following code will make | |
| 291 // the message builder construct the two text parts. | |
| 292 mb.constructTextPart(vmime::mediaType | |
| 293 (vmime::mediaTypes::TEXT, vmime::mediaTypes::TEXT_HTML)); | |
| 294 | |
| 295 // Set contents of the text parts; the message is available in two formats: | |
| 296 // HTML and plain text. The HTML format also includes an embedded image. | |
| 297 vmime::shared_ptr <vmime::htmlTextPart> textPart = | |
| 298 vmime::dynamicCast <vmime::htmlTextPart>(mb.getTextPart()); | |
| 299 | |
| 300 // -- Add the JPEG image (the returned identifier is used to identify the | |
| 301 // -- embedded object in the HTML text, the famous "CID", or "Content-Id"). | |
| 302 // -- Note: you can also read data from a file; see the next example. | |
| 303 const vmime::string id = textPart->addObject("<...image data...>", | |
| 304 vmime::mediaType(vmime::mediaTypes::IMAGE, vmime::mediaTypes::IMAGE_JPEG)); | |
| 305 | |
| 306 // -- Set the text | |
| 307 textPart->setCharset(vmime::charsets::ISO8859_15); | |
| 308 | |
| 309 textPart->setText(vmime::make_shared <vmime::stringContentHandler> | |
| 310 ("This is the <b>HTML text</b>, and the image:<br/>" | |
| 311 "<img src=\"") + id + vmime::string("\"/>")); | |
| 312 | |
| 313 textPart->setPlainText(vmime::make_shared <vmime::stringContentHandler> | |
| 314 ("This is the plain text.")); | |
| 315 \end{lstlisting} | |
| 316 | |
| 317 This will create a message having the following structure: | |
| 318 | |
| 319 \begin{verbatim} | |
| 320 multipart/alternative | |
| 321 text/plain | |
| 322 multipart/related | |
| 323 text/html | |
| 324 image/jpeg | |
| 325 \end{verbatim} | |
| 326 | |
| 327 You can easily tell VMime to read the embedded object data from a file. The | |
| 328 following code opens the file \emph{/path/to/image.jpg}, connects it to an | |
| 329 input stream, then add an embedded object: | |
| 330 | |
| 331 \begin{lstlisting} | |
| 332 vmime::utility::fileSystemFactory* fs = | |
| 333 vmime::platform::getHandler()->getFileSystemFactory(); | |
| 334 | |
| 335 vmime::shared_ptr <vmime::utility::file> imageFile = | |
| 336 fs->create(fs->stringToPath("/path/to/image.jpg")); | |
| 337 | |
| 338 vmime::shared_ptr <vmime::contentHandler> imageCts = | |
| 339 vmime::make_shared <vmime::streamContentHandler> | |
| 340 (imageFile->getFileReader()->getInputStream(), imageFile->getLength()); | |
| 341 | |
| 342 const vmime::string cid = textPart.addObject(imageCts, | |
| 343 vmime::mediaType(vmime::mediaTypes::IMAGE, vmime::mediaTypes::IMAGE_JPEG)); | |
| 344 \end{lstlisting} | |
| 345 | |
| 346 | |
| 347 % ============================================================================ | |
| 348 \section{Working with attachments: the attachment helper} | |
| 349 | |
| 350 The {\vcode attachmentHelper} object allows listing all attachments in a | |
| 351 message, as well as adding new attachments, without using the | |
| 352 {\vcode messageParser} and {\vcode messageBuilders} objects. It can work | |
| 353 directly on messages and body parts. | |
| 354 | |
| 355 To use it, you do not need any knowledge about how attachment parts should | |
| 356 be organized in a MIME message. | |
| 357 | |
| 358 The following code snippet tests if a body part is an attachment, and if so, | |
| 359 extract its contents to the standard output: | |
| 360 | |
| 361 \begin{lstlisting}[caption={Testing if a body part is an attachment}] | |
| 362 vmime::shared_ptr <vmime::bodyPart> part; // suppose we have a body part | |
| 363 | |
| 364 if (vmime::attachmentHelper::isBodyPartAnAttachment(part)) | |
| 365 { | |
| 366 // The body part contains an attachment, get it | |
| 367 vmime::shared_ptr <const vmime::attachment> attach = | |
| 368 attachmentHelper::getBodyPartAttachment(part); | |
| 369 | |
| 370 // Extract attachment data to standard output | |
| 371 vmime::utility::outputStreamAdapter out(std::cout); | |
| 372 attach->getData()->extract(out); | |
| 373 } | |
| 374 \end{lstlisting} | |
| 375 | |
| 376 You can also easily extract all attachments from a message: | |
| 377 | |
| 378 \begin{lstlisting}[caption={Extracting all attachments from a message}] | |
| 379 vmime::shared_ptr <vmime::message> msg; // suppose we have a message | |
| 380 | |
| 381 const std::vector <ref <const attachment> > atts = | |
| 382 attachmentHelper::findAttachmentsInMessage(msg); | |
| 383 \end{lstlisting} | |
| 384 | |
| 385 Finally, the {\vcode attachmentHelper} object can be used to add an | |
| 386 attachment to an existing message, whatever it contains (text parts, | |
| 387 attachments, ...). The algorithm can modify the structure of the | |
| 388 message if needed (eg. add a \emph{multipart/mixed} part if no one | |
| 389 exists in the message). Simply call the {\vcode addAttachment} | |
| 390 function: | |
| 391 | |
| 392 \begin{lstlisting}[caption={Adding an attachment to an existing message}] | |
| 393 vmime::shared_ptr <vmime::message> msg; // suppose we have a message | |
| 394 | |
| 395 // Create an attachment | |
| 396 vmime::shared_ptr <vmime::fileAttachment> att = | |
| 397 vmime::make_shared <vmime::fileAttachment> | |
| 398 ( | |
| 399 /* full path to file */ "/home/vincent/paris.jpg", | |
| 400 /* content type */ vmime::mediaType("image/jpeg), | |
| 401 /* description */ vmime::text("My holidays in Paris") | |
| 402 ); | |
| 403 | |
| 404 // Attach it to the message | |
| 405 vmime::attachmentHelper::addAttachment(msg, att); | |
| 406 \end{lstlisting} | |
| 407 |
