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