|
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...
|