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

created the repository for the thymian project
author ferencd
date Tue, 17 Aug 2021 11:19:54 +0200
parents
children
rev   line source
ferencd@0 1 \chapter{Working with Messaging Services}
ferencd@0 2
ferencd@0 3 % ============================================================================
ferencd@0 4 \section{Introduction}
ferencd@0 5
ferencd@0 6 In addition to parsing and building MIME messages, VMime also offers a lot of
ferencd@0 7 features to work with messaging services. This includes connecting to remote
ferencd@0 8 messaging stores (like IMAP or POP3), local stores (maildir) and transport
ferencd@0 9 services (send messages over SMTP or local sendmail), through an unified
ferencd@0 10 interface (see Figure \ref{uml_messaging_module}). That means that you can
ferencd@0 11 use independently IMAP of POP3 without having to change any line of code.
ferencd@0 12
ferencd@0 13 Source code of {\vexample Example6} covers all features presented in this
ferencd@0 14 chapter, so it is important you take some time to read it.
ferencd@0 15
ferencd@0 16 \begin{figure}
ferencd@0 17 \center\includegraphics[width=0.9\textwidth]
ferencd@0 18 {images/messaging-services.png}\endcenter
ferencd@0 19 \caption{Overall structure of the messaging module}
ferencd@0 20 \label{uml_messaging_module}
ferencd@0 21 \end{figure}
ferencd@0 22
ferencd@0 23 The interface is composed of five classes:
ferencd@0 24
ferencd@0 25 \begin{itemize}
ferencd@0 26 \item {\vcode vmime::net::service}: this is the base interface for a
ferencd@0 27 messaging service. It can be either a store service or a transport
ferencd@0 28 service.
ferencd@0 29
ferencd@0 30 \item {\vcode vmime::net::serviceFactory}: create instances of a service.
ferencd@0 31 This is used internally by the session object (see below).
ferencd@0 32
ferencd@0 33 \item {\vcode vmime::net::store}: interface for a store service. A store
ferencd@0 34 service offers access to a set of folders containing messages. This is
ferencd@0 35 used for IMAP, POP3 and maildir.
ferencd@0 36
ferencd@0 37 \item {\vcode vmime::net::transport}: interface for a transport service.
ferencd@0 38 A transport service is capable of sending messages. This is used for
ferencd@0 39 SMTP and sendmail.
ferencd@0 40
ferencd@0 41 \item {\vcode vmime::net::session}: a session object is used to store the
ferencd@0 42 parameters used by a service (eg. connection parameters). Each service
ferencd@0 43 instance is associated with only one session. The session object is capable
ferencd@0 44 of creating instances of services.
ferencd@0 45 \end{itemize}
ferencd@0 46
ferencd@0 47 The following classes are specific to store services:
ferencd@0 48
ferencd@0 49 \begin{itemize}
ferencd@0 50 \item {\vcode vmime::net::folder}: a folder can either contain other folders
ferencd@0 51 or messages, or both.
ferencd@0 52
ferencd@0 53 \item {\vcode vmime::net::message}: this is the interface for dealing with
ferencd@0 54 messages. For a given message, you can have access to its flags, its MIME
ferencd@0 55 structure and you can also extract the whole message data or given parts (if
ferencd@0 56 supported by the underlying protocol).
ferencd@0 57 \end{itemize}
ferencd@0 58
ferencd@0 59
ferencd@0 60 % ============================================================================
ferencd@0 61 \section{Working with sessions}
ferencd@0 62
ferencd@0 63 \subsection{Setting properties} % --------------------------------------------
ferencd@0 64
ferencd@0 65 Sessions are used to store configuration parameters for services. They
ferencd@0 66 contains a set of typed properties that can modify the behaviour of the
ferencd@0 67 services. Before using a messaging service, you must create and
ferencd@0 68 initialize a session object:
ferencd@0 69
ferencd@0 70 \begin{lstlisting}
ferencd@0 71 vmime::shared_ptr <net::session> theSession = vmime::make_shared <net::session>();
ferencd@0 72 \end{lstlisting}
ferencd@0 73
ferencd@0 74 Session properties include:
ferencd@0 75
ferencd@0 76 \begin{itemize}
ferencd@0 77 \item connection parameters: host and port to connect to;
ferencd@0 78 \item authentication parameters: user credentials required to use the
ferencd@0 79 service (if any);
ferencd@0 80 \item protocol-specific parameters: enable or disable extensions (eg. APOP
ferencd@0 81 support in POP3).
ferencd@0 82 \end{itemize}
ferencd@0 83
ferencd@0 84 Properties are stored using a dotted notation, to specify the service type,
ferencd@0 85 the protocol name, the category and the name of the property:
ferencd@0 86
ferencd@0 87 \begin{verbatim}
ferencd@0 88 {service_type}.{protocol}.category.name
ferencd@0 89 \end{verbatim}
ferencd@0 90
ferencd@0 91 An example of property is \emph{store.pop3.options.apop} (used to enable or
ferencd@0 92 disable the use of APOP authentication). The \emph{store.pop3} part is called
ferencd@0 93 the \emph{prefix}. This allow specifying different values for the same
ferencd@0 94 property depending on the protocol used.
ferencd@0 95
ferencd@0 96 The session properties are stored in a {\vcode vmime::propertySet} object.
ferencd@0 97 To set the value of a property, you can use either:
ferencd@0 98
ferencd@0 99 \begin{lstlisting}
ferencd@0 100 theSession->getProperties().setProperty("property-name", value);
ferencd@0 101 \end{lstlisting}
ferencd@0 102
ferencd@0 103 or:
ferencd@0 104
ferencd@0 105 \begin{lstlisting}
ferencd@0 106 theSession->getProperties()["property-name"] = value;
ferencd@0 107 \end{lstlisting}
ferencd@0 108
ferencd@0 109
ferencd@0 110 \subsection{Available properties} % ------------------------------------------
ferencd@0 111
ferencd@0 112 Following is a list of available properties and the protocols they apply to,
ferencd@0 113 as the time of writing this documentation\footnote{You can get an up-to-date
ferencd@0 114 list of the properties by running \vexample{Example7}}. For better clarity,
ferencd@0 115 the prefixes do not appear in this table.
ferencd@0 116
ferencd@0 117 \begin{table}[!ht]
ferencd@0 118 \noindent\begin{tabularx}{1.0\textwidth}{|l|c|X|c|c|c|c|c|c|c|c|}
ferencd@0 119 \hline
ferencd@0 120 {\bf Property name} &
ferencd@0 121 {\bf Type} &
ferencd@0 122 {\bf Description} &
ferencd@0 123 \verti{\bf POP3} &
ferencd@0 124 \verti{\bf POP3S} &
ferencd@0 125 \verti{\bf IMAP} &
ferencd@0 126 \verti{\bf IMAPS} &
ferencd@0 127 \verti{\bf SMTP} &
ferencd@0 128 \verti{\bf SMTPS} &
ferencd@0 129 \verti{\bf maildir} &
ferencd@0 130 \verti{\bf sendmail} \\
ferencd@0 131 \hline
ferencd@0 132 \hline
ferencd@0 133 options.sasl & bool & Set to {\vcode true} to use SASL authentication, if
ferencd@0 134 available. & \vdot & \vdot & \vdot & \vdot & \vdot & \vdot & & \\
ferencd@0 135 \hline
ferencd@0 136 options.sasl.fallback & bool & Fail if SASL authentication failed (do not
ferencd@0 137 try other authentication mechanisms). & \vdot & \vdot & \vdot & \vdot &
ferencd@0 138 \vdot & \vdot & & \\
ferencd@0 139 \hline
ferencd@0 140 auth.username\footnote{You should use authenticators
ferencd@0 141 instead.\label{fn_auth_username}} & string & Set the username of the account
ferencd@0 142 to connect to. & \vdot & \vdot & \vdot & \vdot & \vdot & \vdot & & \\
ferencd@0 143 \hline
ferencd@0 144 auth.password\footref{fn_auth_username} & string & Set the password of the
ferencd@0 145 account. & \vdot & \vdot & \vdot & \vdot & \vdot & \vdot & & \\
ferencd@0 146 \hline
ferencd@0 147 connection.tls & bool & Set to {\vcode true} to start a secured connection
ferencd@0 148 using STARTTLS extension, if available. & \vdot & & \vdot & & \vdot & & & \\
ferencd@0 149 \hline
ferencd@0 150 connection.tls.required & bool & Fail if a secured connection cannot be
ferencd@0 151 started. & \vdot & & \vdot & & \vdot & & & \\
ferencd@0 152 \hline
ferencd@0 153 server.address & string & Server host name or IP address. &\vdot & \vdot &
ferencd@0 154 \vdot & \vdot & \vdot & \vdot & & \\
ferencd@0 155 \hline
ferencd@0 156 server.port & int & Server port. & \vdot & \vdot & \vdot & \vdot &
ferencd@0 157 \vdot & \vdot & & \\
ferencd@0 158 \hline
ferencd@0 159 server.rootpath & string & Root directory for mail repository (eg.
ferencd@0 160 \emph{/home/vincent/Mail}). & & & & & & & \vdot & \\
ferencd@0 161 \hline
ferencd@0 162 \end{tabularx}
ferencd@0 163 \caption{Properties common to all protocols}
ferencd@0 164 \end{table}
ferencd@0 165
ferencd@0 166 \newpage
ferencd@0 167 These are the protocol-specific options:
ferencd@0 168
ferencd@0 169 \begin{table}[!ht]
ferencd@0 170 \noindent\begin{tabularx}{1.0\textwidth}{|l|c|X|}
ferencd@0 171 \hline
ferencd@0 172 {\bf Property name} &
ferencd@0 173 {\bf Type} &
ferencd@0 174 {\bf Description} \\
ferencd@0 175 % POP3/POP3S
ferencd@0 176 \hline
ferencd@0 177 \multicolumn{3}{|c|}{POP3, POP3S} \\
ferencd@0 178 \hline
ferencd@0 179 store.pop3.options.apop & bool & Enable or disable authentication with
ferencd@0 180 APOP (if SASL is enabled, this occurs after all SASL mechanisms have been
ferencd@0 181 tried). \\
ferencd@0 182 \hline
ferencd@0 183 store.pop3.options.apop.fallback & bool & If set to {\vcode true} and
ferencd@0 184 APOP fails, the authentication process fails (ie. unsecure plain text
ferencd@0 185 authentication is not used). \\
ferencd@0 186 \hline
ferencd@0 187 % SMTP
ferencd@0 188 \multicolumn{3}{|c|}{SMTP, SMTPS} \\
ferencd@0 189 \hline
ferencd@0 190 transport.smtp.options.need-authentication & bool & Set to \emph{true} if
ferencd@0 191 the server requires to authenticate before sending messages. \\
ferencd@0 192 \hline
ferencd@0 193 transport.smtp.options.pipelining & bool & Set to {\vcode false} to disable
ferencd@0 194 command pipelining, if the server supports it (default is {\vcode true}). \\
ferencd@0 195 \hline
ferencd@0 196 transport.smtp.options.chunking & bool & Set to {\vcode false} to disable
ferencd@0 197 CHUNKING extension, if the server supports it (default is {\vcode true}). \\
ferencd@0 198 \hline
ferencd@0 199 % sendmail
ferencd@0 200 \multicolumn{3}{|c|}{sendmail} \\
ferencd@0 201 \hline
ferencd@0 202 transport.sendmail.binpath & string & The path to the \emph{sendmail}
ferencd@0 203 executable on your system. The default is the one found by the configuration
ferencd@0 204 script when VMime was built. \\
ferencd@0 205 \hline
ferencd@0 206 \end{tabularx}
ferencd@0 207 \caption{Protocol-specific options}
ferencd@0 208 \end{table}
ferencd@0 209
ferencd@0 210
ferencd@0 211 \subsection{Instanciating services} % ----------------------------------------
ferencd@0 212
ferencd@0 213 You can create a service either by specifying its protocol name, or by
ferencd@0 214 specifying the URL of the service. Creation by name is deprecated so
ferencd@0 215 this chapter only presents the latter option.
ferencd@0 216
ferencd@0 217 The URL scheme for connecting to services is:
ferencd@0 218
ferencd@0 219 \begin{verbatim}
ferencd@0 220 protocol://[username[:password]@]host[:port]/[root-path]
ferencd@0 221 \end{verbatim}
ferencd@0 222
ferencd@0 223 \vnote{For local services (ie. \emph{sendmail} and \emph{maildir}), the host
ferencd@0 224 part is not used, but it must not be empty (you can use "localhost").}
ferencd@0 225
ferencd@0 226 The following table shows an example URL for each service:
ferencd@0 227
ferencd@0 228 \noindent\begin{tabularx}{1.0\textwidth}{|c|X|}
ferencd@0 229 \hline
ferencd@0 230 {\bf Service} &
ferencd@0 231 {\bf Connection URL} \\
ferencd@0 232 \hline
ferencd@0 233 imap, imaps & {\tt imap://imap.example.com},
ferencd@0 234 {\tt imaps://vincent:pass@example.com} \\
ferencd@0 235 \hline
ferencd@0 236 pop3, pop3s & {\tt pop3://pop3.example.com} \\
ferencd@0 237 \hline
ferencd@0 238 smtp, smtps & {\tt smtp://smtp.example.com} \\
ferencd@0 239 \hline
ferencd@0 240 maildir & {\tt maildir://localhost/home/vincent/Mail} (host not used) \\
ferencd@0 241 \hline
ferencd@0 242 sendmail & {\tt sendmail://localhost} (host not used, always localhost) \\
ferencd@0 243 \hline
ferencd@0 244 \end{tabularx}
ferencd@0 245
ferencd@0 246 \newpage
ferencd@0 247
ferencd@0 248 When you have the connection URL, instanciating the service is quite simple.
ferencd@0 249 Depending on the type of service, you will use either {\vcode getStore()} or
ferencd@0 250 {\vcode getTransport()}. For example, for store services, use:
ferencd@0 251
ferencd@0 252 \begin{lstlisting}
ferencd@0 253 vmime::utility:url url("imap://user:pass@imap.example.com");
ferencd@0 254 vmime::shared_ptr <vmime::net::store> st = sess->getStore(url);
ferencd@0 255 \end{lstlisting}
ferencd@0 256
ferencd@0 257 and for transport services:
ferencd@0 258
ferencd@0 259 \begin{lstlisting}
ferencd@0 260 vmime::utility:url url("smtp://smtp.example.com");
ferencd@0 261 vmime::shared_ptr <vmime::net::transport> tr = sess->getTransport(url);
ferencd@0 262 \end{lstlisting}
ferencd@0 263
ferencd@0 264
ferencd@0 265 % ============================================================================
ferencd@0 266 \section{User credentials and authenticators}
ferencd@0 267
ferencd@0 268 Some services need some user credentials (eg. username and password) to open
ferencd@0 269 a session. In VMime, user credentials can be specified in the session
ferencd@0 270 properties or by using a custom authenticator (callback).
ferencd@0 271
ferencd@0 272 \begin{lstlisting}[caption={Setting user credentials using session
ferencd@0 273 properties}]
ferencd@0 274 vmime::shared_ptr <vmime::net::session> sess; // Suppose we have a session
ferencd@0 275
ferencd@0 276 sess->getProperties()["store.imap.auth.username"] = "vincent";
ferencd@0 277 sess->getProperties()["store.imap.auth.password"] = "my-password";
ferencd@0 278 \end{lstlisting}
ferencd@0 279
ferencd@0 280 Although not recommended, you can also specify username and password
ferencd@0 281 directly in the connection URL,
ferencd@0 282 ie: \emph{imap://username:password@imap.example.com/}. This works only for
ferencd@0 283 services requiring an username and a password as user credentials, and no
ferencd@0 284 other information.
ferencd@0 285
ferencd@0 286 Sometimes, it may not be very convenient to set username/password in the
ferencd@0 287 session properties, or not possible (eg. extended SASL mechanisms) . That's
ferencd@0 288 why VMime offers an alternate way of getting user credentials: the
ferencd@0 289 {\vcode authenticator} object. Basically, an authenticator is an object that
ferencd@0 290 can return user credentials on-demand (like a callback).
ferencd@0 291
ferencd@0 292 Currently, there are two types of authenticator in VMime: a basic
ferencd@0 293 authenticator (class {\vcode vmime::security::authenticator}) and, if SASL
ferencd@0 294 support is enabled, a SASL authenticator
ferencd@0 295 (class {\vcode vmime::security::sasl::SASLAuthenticator}). Usually, you
ferencd@0 296 should use the default implementations, or at least make your own
ferencd@0 297 implementation inherit from them.
ferencd@0 298
ferencd@0 299 The following example shows how to use a custom authenticator to request
ferencd@0 300 the user to enter her/his credentials:
ferencd@0 301
ferencd@0 302 \begin{lstlisting}[caption={A simple interactive authenticator}]
ferencd@0 303 class myAuthenticator : public vmime::security::defaultAuthenticator
ferencd@0 304 {
ferencd@0 305 const string getUsername() const
ferencd@0 306 {
ferencd@0 307 std::cout << "Enter your username: " << std::endl;
ferencd@0 308
ferencd@0 309 vmime::string res;
ferencd@0 310 std::getline(std::cin, res);
ferencd@0 311
ferencd@0 312 return res;
ferencd@0 313 }
ferencd@0 314
ferencd@0 315 const string getPassword() const
ferencd@0 316 {
ferencd@0 317 std::cout << "Enter your password: " << std::endl;
ferencd@0 318
ferencd@0 319 vmime::string res;
ferencd@0 320 std::getline(std::cin, res);
ferencd@0 321
ferencd@0 322 return res;
ferencd@0 323 }
ferencd@0 324 };
ferencd@0 325 \end{lstlisting}
ferencd@0 326
ferencd@0 327 This is how to use it:
ferencd@0 328
ferencd@0 329 \begin{lstlisting}
ferencd@0 330 // First, create a session
ferencd@0 331 vmime::shared_ptr <vmime::net::session> sess =
ferencd@0 332 vmime::make_shared <vmime::net::session>();
ferencd@0 333
ferencd@0 334 // Next, initialize a service which will use our authenticator
ferencd@0 335 vmime::shared_ptr <vmime::net::store> st =
ferencd@0 336 sess->getStore(vmime::utility::url("imap://imap.example.com"),
ferencd@0 337 /* use our authenticator */ vmime::make_shared <myAuthenticator>());
ferencd@0 338 \end{lstlisting}
ferencd@0 339
ferencd@0 340 \vnote{An authenticator object should be used with one and only one service
ferencd@0 341 at a time. This is required because the authentication process may need to
ferencd@0 342 retrieve the service name (SASL).}
ferencd@0 343
ferencd@0 344 Of course, this example is quite simplified. For example, if several
ferencd@0 345 authentication mechanisms are tried, the user may be requested to enter the
ferencd@0 346 same information multiple times. See {\vexample Example6} for a more complex
ferencd@0 347 implementation of an authenticator, with caching support.
ferencd@0 348
ferencd@0 349 If you want to use SASL (ie. if \emph{options.sasl} is set to \emph{true}),
ferencd@0 350 your authenticator must inherit from
ferencd@0 351 {\vcode vmime::security::sasl::SASLAuthenticator} or
ferencd@0 352 {\vcode vmime::security::sasl::defaultSASLAuthenticator}, even if you do not
ferencd@0 353 use the SASL-specific methods {\vcode getAcceptableMechanisms()} and
ferencd@0 354 {\vcode setSASLMechanism()}. Have a look at {\vexample Example6} to see an
ferencd@0 355 implementation of an SASL authenticator.
ferencd@0 356
ferencd@0 357 \begin{lstlisting}[caption={A simple SASL authenticator}]
ferencd@0 358 class mySASLAuthenticator : public vmime::security::sasl::defaultSASLAuthenticator
ferencd@0 359 {
ferencd@0 360 typedef vmime::security::sasl::SASLMechanism mechanism; // save us typing
ferencd@0 361
ferencd@0 362 const std::vector <vmime::shared_ptr <mechanism> > getAcceptableMechanisms
ferencd@0 363 (const std::vector <vmime::shared_ptr <mechanism> >& available,
ferencd@0 364 vmime::shared_ptr <mechanism> suggested) const
ferencd@0 365 {
ferencd@0 366 // Here, you can sort the SASL mechanisms in the order they will be
ferencd@0 367 // tried. If no SASL mechanism is acceptable (ie. for example, not
ferencd@0 368 // enough secure), you can return an empty list.
ferencd@0 369 //
ferencd@0 370 // If you do not want to bother with this, you can simply return
ferencd@0 371 // the default list, which is ordered by security strength.
ferencd@0 372 return defaultSASLAuthenticator::
ferencd@0 373 getAcceptableMechanisms(available, suggested);
ferencd@0 374 }
ferencd@0 375
ferencd@0 376 void setSASLMechanism(vmime::shared_ptr <mechanism> mech)
ferencd@0 377 {
ferencd@0 378 // This is called when the authentication process is going to
ferencd@0 379 // try the specified mechanism.
ferencd@0 380 //
ferencd@0 381 // The mechanism name is in mech->getName()
ferencd@0 382
ferencd@0 383 defaultSASLAuthenticator::setSASLMechanism(mech);
ferencd@0 384 }
ferencd@0 385
ferencd@0 386 // ...implement getUsername() and getPassword()...
ferencd@0 387 };
ferencd@0 388 \end{lstlisting}
ferencd@0 389
ferencd@0 390
ferencd@0 391 % ============================================================================
ferencd@0 392 \section{Using transport service}
ferencd@0 393
ferencd@0 394 You have two possibilities for giving message data to the service when you
ferencd@0 395 want to send a message:
ferencd@0 396
ferencd@0 397 \begin{itemize}
ferencd@0 398 \item either you have a reference to a message (type {\vcode vmime::message})
ferencd@0 399 and you can simply call {\vcode send(msg)};
ferencd@0 400 \item or you only have raw message data (as a string, for example), and you
ferencd@0 401 have to call the second overload of {\vcode send()}, which takes additional
ferencd@0 402 parameters (corresponding to message envelope);
ferencd@0 403 \end{itemize}
ferencd@0 404
ferencd@0 405 The following example illustrates the use of a transport service to send a
ferencd@0 406 message using the second method:
ferencd@0 407
ferencd@0 408 \begin{lstlisting}[caption={Using a transport service}]
ferencd@0 409 const vmime::string msgData =
ferencd@0 410 "From: me@example.org \r\n"
ferencd@0 411 "To: you@example.org \r\n"
ferencd@0 412 "Date: Sun, Oct 30 2005 17:06:42 +0200 \r\n"
ferencd@0 413 "Subject: Test \r\n"
ferencd@0 414 "\r\n"
ferencd@0 415 "Message body";
ferencd@0 416
ferencd@0 417 // Create a new session
ferencd@0 418 vmime::utility::url url("smtp://example.com");
ferencd@0 419
ferencd@0 420 vmime::shared_ptr <vmime::net::session> sess =
ferencd@0 421 vmime::make_shared <vmime::net::session>();
ferencd@0 422
ferencd@0 423 // Create an instance of the transport service
ferencd@0 424 vmime::shared_ptr <vmime::net::transport> tr = sess->getTransport(url);
ferencd@0 425
ferencd@0 426 // Connect it
ferencd@0 427 tr->connect();
ferencd@0 428
ferencd@0 429 // Send the message
ferencd@0 430 vmime::utility::inputStreamStringAdapter is(msgData);
ferencd@0 431
ferencd@0 432 vmime::mailbox from("me@example.org");
ferencd@0 433 vmime::mailboxList to;
ferencd@0 434 to.appendMailbox(vmime::make_shared <vmime::mailbox>("you@example.org"));
ferencd@0 435
ferencd@0 436 tr->send(
ferencd@0 437 /* expeditor */ from,
ferencd@0 438 /* recipient(s) */ to,
ferencd@0 439 /* data */ is,
ferencd@0 440 /* total length */ msgData.length());
ferencd@0 441
ferencd@0 442 // We have finished using the service
ferencd@0 443 tr->disconnect();
ferencd@0 444 \end{lstlisting}
ferencd@0 445
ferencd@0 446 \vnote{Exceptions can be thrown at any time when using a service. For better
ferencd@0 447 clarity, exceptions are not caught here, but be sure to catch them in your own
ferencd@0 448 application to provide error feedback to the user.}
ferencd@0 449
ferencd@0 450 If you use SMTP, you can enable authentication by setting some properties
ferencd@0 451 on the session object ({\vcode service::setProperty()} is a shortcut for
ferencd@0 452 setting properties on the session with the correct prefix):
ferencd@0 453
ferencd@0 454 \begin{lstlisting}
ferencd@0 455 tr->setProperty("options.need-authentication", true);
ferencd@0 456 tr->setProperty("auth.username", "user");
ferencd@0 457 tr->setProperty("auth.password", "password");
ferencd@0 458 \end{lstlisting}
ferencd@0 459
ferencd@0 460
ferencd@0 461 % ============================================================================
ferencd@0 462 \section{Using store service}
ferencd@0 463
ferencd@0 464 \subsection{Connecting to a store} % -----------------------------------------
ferencd@0 465
ferencd@0 466 The first basic step for using a store service is to connect to it. The
ferencd@0 467 following example shows how to initialize a session and instanciate the
ferencd@0 468 store service:
ferencd@0 469
ferencd@0 470 \begin{lstlisting}[caption={Connecting to a store service}]
ferencd@0 471 // Create a new session
ferencd@0 472 vmime::utility::url url("imap://vincent:password@imap:example.org");
ferencd@0 473
ferencd@0 474 vmime::shared_ptr <vmime::net::session> sess =
ferencd@0 475 vmime::make_shared <vmime::net::session>();
ferencd@0 476
ferencd@0 477 // Create an instance of the transport service
ferencd@0 478 vmime::shared_ptr <vmime::net::store> store = sess->getStore(url);
ferencd@0 479
ferencd@0 480 // Connect it
ferencd@0 481 store->connect();
ferencd@0 482 \end{lstlisting}
ferencd@0 483
ferencd@0 484 \vnote{{\vexample Example6} contains a more complete example for connecting
ferencd@0 485 to a store service, with support for a custom authenticator.}
ferencd@0 486
ferencd@0 487 \subsection{Opening a folder} % ----------------------------------------------
ferencd@0 488
ferencd@0 489 You can open a folder using two different access modes: either in
ferencd@0 490 \emph{read-only} mode (where you can only read message flags and contents), or
ferencd@0 491 in \emph{read-write} mode (where you can read messages, but also delete them
ferencd@0 492 or add new ones). When you have a reference to a folder, simply call the
ferencd@0 493 {\vcode open()} method with the desired access mode:
ferencd@0 494
ferencd@0 495 \begin{lstlisting}
ferencd@0 496 folder->open(vmime::net::folder::MODE_READ_WRITE);
ferencd@0 497 \end{lstlisting}
ferencd@0 498
ferencd@0 499 \vnote{Not all stores support the \emph{read-write} mode. By default, if the
ferencd@0 500 \emph{read-write} mode is not available, the folder silently fall backs on
ferencd@0 501 the \emph{read-only} mode, unless the \emph{failIfModeIsNotAvailable} argument
ferencd@0 502 to {\vcode open()} is set to true.}
ferencd@0 503
ferencd@0 504 Call {\vcode getDefaultFolder()} on the store to obtain a reference to the
ferencd@0 505 default folder, which is usually the INBOX folder (where messages arrive when
ferencd@0 506 they are received).
ferencd@0 507
ferencd@0 508 You can also open a specific folder by specifying its path. The following
ferencd@0 509 example will open a folder named \emph{bar}, which is a child of \emph{foo}
ferencd@0 510 in the root folder:
ferencd@0 511
ferencd@0 512 \begin{lstlisting}[caption={Opening a folder from its path}]
ferencd@0 513 vmime::net::folder::path path;
ferencd@0 514 path /= vmime::net::folder::path::component("foo");
ferencd@0 515 path /= vmime::net::folder::path::component("bar");
ferencd@0 516
ferencd@0 517 vmime::shared_ptr <vmime::net::folder> fld = store->getFolder(path);
ferencd@0 518 fld->open(vmime::net::folder::MODE_READ_WRITE);
ferencd@0 519 \end{lstlisting}
ferencd@0 520
ferencd@0 521 \vnote{You can specify a path as a string as there is no way to get the
ferencd@0 522 separator used to delimitate path components. Always use {\vcode operator/=}
ferencd@0 523 or {\vcode appendComponent}.}
ferencd@0 524
ferencd@0 525 \vnote{Path components are of type {\vcode vmime::word}, which means that
ferencd@0 526 VMime supports folder names with extended characters, not only 7-bit
ferencd@0 527 US-ASCII. However, be careful that this may not be supported by the
ferencd@0 528 underlying store protocol (IMAP supports it, because it uses internally a
ferencd@0 529 modified UTF-7 encoding).}
ferencd@0 530
ferencd@0 531 \subsection{Fetching messages} % ---------------------------------------------
ferencd@0 532
ferencd@0 533 You can fetch some information about a message without having to download the
ferencd@0 534 whole message. Moreover, folders support fetching for multiple messages in
ferencd@0 535 a single request, for better performance. The following items are currently
ferencd@0 536 available for fetching:
ferencd@0 537
ferencd@0 538 \begin{itemize}
ferencd@0 539 \item {\bf envelope}: sender, recipients, date and subject;
ferencd@0 540 \item {\bf structure}: MIME structure of the message;
ferencd@0 541 \item {\bf content-info}: content-type of the root part;
ferencd@0 542 \item {\bf flags}: message flags;
ferencd@0 543 \item {\bf size}: message size;
ferencd@0 544 \item {\bf header}: retrieve all the header fields of a message;
ferencd@0 545 \item {\bf uid}: unique identifier of a message;
ferencd@0 546 \item {\bf importance}: fetch header fields suitable for use with
ferencd@0 547 {\vcode misc::importanceHelper}.
ferencd@0 548 \end{itemize}
ferencd@0 549
ferencd@0 550 \vnote{Not all services support all fetchable items. Call
ferencd@0 551 {\vcode getFetchCapabilities()} on a folder to know which information can be
ferencd@0 552 fetched by a service.}
ferencd@0 553
ferencd@0 554 The following code shows how to list all the messages in a folder, and
ferencd@0 555 retrieve basic information to show them to the user:
ferencd@0 556
ferencd@0 557 \begin{lstlisting}[caption={Fetching information about multiple messages}]
ferencd@0 558 std::vector <ref <vmime::net::message> > allMessages =
ferencd@0 559 folder->getMessages(vmime::net::messageSet::byNumber(1, -1));
ferencd@0 560 // -1 is a special value to mean "the number of the last message in the folder"
ferencd@0 561
ferencd@0 562 folder->fetchMessages(allMessages,
ferencd@0 563 vmime::net::fetchAttributes::FLAGS |
ferencd@0 564 vmime::net::fetchAttributes::ENVELOPE);
ferencd@0 565
ferencd@0 566 for (unsigned int i = 0 ; i < allMessages.size() ; ++i)
ferencd@0 567 {
ferencd@0 568 vmime::shared_ptr <vmime::net::message> msg = allMessages[i];
ferencd@0 569
ferencd@0 570 const int flags = msg->getFlags();
ferencd@0 571
ferencd@0 572 std::cout << "Message " << i << ":" << std::endl;
ferencd@0 573
ferencd@0 574 if (flags & vmime::net::message::FLAG_SEEN)
ferencd@0 575 std::cout << " - is read" << std::endl;
ferencd@0 576 if (flags & vmime::net::message::FLAG_DELETED)
ferencd@0 577 std::cout << " - is deleted" << std::endl;
ferencd@0 578
ferencd@0 579 vmime::shared_ptr <const vmime::header> hdr = msg->getHeader();
ferencd@0 580
ferencd@0 581 std::cout << " - sent on " << hdr->Date()->generate() << std::endl;
ferencd@0 582 std::cout << " - sent by " << hdr->From()->generate() << std::endl;
ferencd@0 583 }
ferencd@0 584 \end{lstlisting}
ferencd@0 585
ferencd@0 586 IMAP supports fetching specific header fields of a message. Here is how to use
ferencd@0 587 the {\vcode fetchAttributes} object to do it:
ferencd@0 588
ferencd@0 589 \begin{lstlisting}[caption={Using fetchAttributes object to fetch specific header fields of a message}]
ferencd@0 590
ferencd@0 591 // Fetch message flags and the "Received" and "X-Mailer" header fields
ferencd@0 592 vmime::net::fetchAttributes fetchAttribs;
ferencd@0 593 fetchAttribs.add(vmime::net::fetchAttributes::FLAGS);
ferencd@0 594 fetchAttribs.add("Received");
ferencd@0 595 fetchAttribs.add("X-Mailer");
ferencd@0 596
ferencd@0 597 folder->fetchMessages(allMessages, fetchAttribs);
ferencd@0 598 \end{lstlisting}
ferencd@0 599
ferencd@0 600
ferencd@0 601 \subsection{Extracting messages and parts}
ferencd@0 602
ferencd@0 603 To extract the whole contents of a message (including headers), use the
ferencd@0 604 {\vcode extract()} method on a {\vcode vmime::net::message} object. The
ferencd@0 605 following example extracts the first message in the default folder:
ferencd@0 606
ferencd@0 607 \begin{lstlisting}[caption={Extracting messages}]
ferencd@0 608 // Get a reference to the folder and to its first message
ferencd@0 609 vmime::shared_ptr <vmime::net::folder> folder = store->getDefaultFolder();
ferencd@0 610 vmime::shared_ptr <vmime::net::message> msg = folder->getMessage(1);
ferencd@0 611
ferencd@0 612 // Write the message contents to the standard output
ferencd@0 613 vmime::utility::outputStreamAdapter out(std::cout);
ferencd@0 614 msg->extract(out);
ferencd@0 615 \end{lstlisting}
ferencd@0 616
ferencd@0 617 Some protocols (like IMAP) also support the extraction of specific MIME parts
ferencd@0 618 of a message without downloading the whole message. This can save bandwidth
ferencd@0 619 and time. The method {\vcode extractPart()} is used in this case:
ferencd@0 620
ferencd@0 621 \begin{lstlisting}[caption={Extracting a specific MIME part of a message}]
ferencd@0 622 // Fetching structure is required before extracting a part
ferencd@0 623 folder->fetchMessage(msg, vmime::net::fetchAttributes::STRUCTURE);
ferencd@0 624
ferencd@0 625 // Now, we can extract the part
ferencd@0 626 msg->extractPart(msg->getStructure()->getPartAt(0)->getPartAt(1));
ferencd@0 627 \end{lstlisting}
ferencd@0 628
ferencd@0 629 Suppose we have a message with the following structure:
ferencd@0 630
ferencd@0 631 \begin{verbatim}
ferencd@0 632 multipart/mixed
ferencd@0 633 text/html
ferencd@0 634 image/jpeg [*]
ferencd@0 635 \end{verbatim}
ferencd@0 636
ferencd@0 637 The previous example will extract the header and body of the \emph{image/jpeg}
ferencd@0 638 part.
ferencd@0 639
ferencd@0 640 \subsection{Deleting messages} % ---------------------------------------------
ferencd@0 641
ferencd@0 642 The following example will delete the second and the third message from the
ferencd@0 643 store.
ferencd@0 644
ferencd@0 645 \begin{lstlisting}[caption={Deleting messages}]
ferencd@0 646 vmime::shared_ptr <vmime::net::folder> folder = store->getDefaultFolder();
ferencd@0 647
ferencd@0 648 folder->deleteMessages(vmime::net::messageSet::byNumber(/* from */ 2, /* to */ 3));
ferencd@0 649
ferencd@0 650 // This is equivalent
ferencd@0 651 std::vector <int> nums;
ferencd@0 652 nums.push_back(2);
ferencd@0 653 nums.push_back(3);
ferencd@0 654 folder->deleteMessages(vmime::net::messageSet::byNumber(nums));
ferencd@0 655
ferencd@0 656 // This is also equivalent (but will require 2 roundtrips to server)
ferencd@0 657 folder->deleteMessages(vmime::net::messageSet::byNumber(2));
ferencd@0 658 folder->deleteMessages(vmime::net::messageSet::byNumber(2)); // renumbered, 3 becomes 2
ferencd@0 659 \end{lstlisting}
ferencd@0 660
ferencd@0 661 \subsection{Events} % --------------------------------------------------------
ferencd@0 662
ferencd@0 663 As a result of executing some operation (or from time to time, even if no
ferencd@0 664 operation has been performed), a store service can send events to notify you
ferencd@0 665 that something has changed (eg. the number of messages in a folder). These
ferencd@0 666 events may allow you to update the user interface associated to a message
ferencd@0 667 store.
ferencd@0 668
ferencd@0 669 Currently, there are three types of event:
ferencd@0 670
ferencd@0 671 \begin{itemize}
ferencd@0 672 \item {\bf message change}: sent when the number of messages in a folder
ferencd@0 673 has changed (ie. some messages have been added or removed);
ferencd@0 674 \item {\bf message count change}: sent when one or more message(s) have
ferencd@0 675 changed (eg. flags or deleted status);
ferencd@0 676 \item {\bf folder change}: sent when a folder has been created, renamed or
ferencd@0 677 deleted.
ferencd@0 678 \end{itemize}
ferencd@0 679
ferencd@0 680 You can register a listener for each event type by using the corresponding
ferencd@0 681 methods on a {\vcode folder} object: {\vcode addMessageChangedListener()},
ferencd@0 682 {\vcode addMessageCountListener()} or {\vcode addFolderListener()}. For more
ferencd@0 683 information, please read the class documentation for
ferencd@0 684 {\vcode vmime::net::events} namespace.
ferencd@0 685
ferencd@0 686
ferencd@0 687 % ============================================================================
ferencd@0 688 \section{Handling timeouts}
ferencd@0 689
ferencd@0 690 Unexpected errors can occur while messaging services are performing
ferencd@0 691 operations and waiting a response from the server (eg. server stops
ferencd@0 692 responding, network link falls down). As all operations as synchronous,
ferencd@0 693 they can be ``blocked'' a long time before returning (in fact, they loop
ferencd@0 694 until they either receive a response from the server, or the underlying
ferencd@0 695 socket system returns an error).
ferencd@0 696
ferencd@0 697 VMime provides a mechanism to control the duration of operations. This
ferencd@0 698 mechanism allows the program to cancel an operation that is currently
ferencd@0 699 running.
ferencd@0 700
ferencd@0 701 An interface called {\vcode timeoutHandler} is provided:
ferencd@0 702
ferencd@0 703 \begin{lstlisting}
ferencd@0 704 class timeoutHandler : public object
ferencd@0 705 {
ferencd@0 706 /** Called to test if the time limit has been reached.
ferencd@0 707 *
ferencd@0 708 * @return true if the timeout delay is elapsed
ferencd@0 709 */
ferencd@0 710 virtual const bool isTimeOut() = 0;
ferencd@0 711
ferencd@0 712 /** Called to reset the timeout counter.
ferencd@0 713 */
ferencd@0 714 virtual void resetTimeOut() = 0;
ferencd@0 715
ferencd@0 716 /** Called when the time limit has been reached (when
ferencd@0 717 * isTimeOut() returned true).
ferencd@0 718 *
ferencd@0 719 * @return true to continue (and reset the timeout)
ferencd@0 720 * or false to cancel the current operation
ferencd@0 721 */
ferencd@0 722 virtual const bool handleTimeOut() = 0;
ferencd@0 723 };
ferencd@0 724 \end{lstlisting}
ferencd@0 725
ferencd@0 726 While the operation runs, the service calls {\vcode isTimeout()} at variable
ferencd@0 727 intervals. If the {\vcode isTimeout()} function returns {\vcode true},
ferencd@0 728 then {\vcode handleTimeout()} is called. If the {\vcode handleTimeout()}
ferencd@0 729 function returns {\vcode false}, the operation is cancelled and
ferencd@0 730 an {\vcode operation\_timed\_out} exception is thrown. Else, if
ferencd@0 731 {\vcode handleTimeout()} returns true, the operation continues and the
ferencd@0 732 timeout counter is reset.
ferencd@0 733 The function {\vcode resetTimeout()} is called each time data has
ferencd@0 734 been received from the server to reset the timeout delay.
ferencd@0 735
ferencd@0 736 When using a service, a default timeout handler is set: if an operation
ferencd@0 737 is blocked for more than 30 seconds (ie. network link is down and no data
ferencd@0 738 was received since 30 seconds), an {\vcode operation\_timed\_out} exception
ferencd@0 739 is thrown.
ferencd@0 740
ferencd@0 741 The following example shows how to implement a simple timeout handler:
ferencd@0 742
ferencd@0 743 \begin{lstlisting}[caption={Implementing a simple timeout handler}]
ferencd@0 744 class myTimeoutHandler : public vmime::net::timeoutHandler
ferencd@0 745 {
ferencd@0 746 public:
ferencd@0 747
ferencd@0 748 myTimeoutHandler()
ferencd@0 749 {
ferencd@0 750 m_startTime = time(NULL);
ferencd@0 751 }
ferencd@0 752
ferencd@0 753 const bool isTimeOut()
ferencd@0 754 {
ferencd@0 755 return (time(NULL) >= m_startTime + 30); // 30 seconds timeout
ferencd@0 756 }
ferencd@0 757
ferencd@0 758 void resetTimeOut()
ferencd@0 759 {
ferencd@0 760 m_startTime = time(NULL);
ferencd@0 761 }
ferencd@0 762
ferencd@0 763 const bool handleTimeOut()
ferencd@0 764 {
ferencd@0 765 std::cout << "Operation timed out." << std::endl;
ferencd@0 766 << "Press [Y] to continue, or [N] to "
ferencd@0 767 << "cancel the operation." << std::endl;
ferencd@0 768
ferencd@0 769 std::string response;
ferencd@0 770 std::cin >> response;
ferencd@0 771
ferencd@0 772 return (response == "y" || response == "Y");
ferencd@0 773 }
ferencd@0 774
ferencd@0 775 private:
ferencd@0 776
ferencd@0 777 time_t m_startTime;
ferencd@0 778 };
ferencd@0 779 \end{lstlisting}
ferencd@0 780
ferencd@0 781 To make the service use your timeout handler, you need to write a factory
ferencd@0 782 class, to allow the service to create instances of the handler class. This
ferencd@0 783 is required because the service can use several connections to the server
ferencd@0 784 simultaneously, and each connection needs its own timeout handler.
ferencd@0 785
ferencd@0 786 \begin{lstlisting}
ferencd@0 787 class myTimeoutHandlerFactory : public vmime::net::timeoutHandlerFactory
ferencd@0 788 {
ferencd@0 789 public:
ferencd@0 790
ferencd@0 791 ref <timeoutHandler> create()
ferencd@0 792 {
ferencd@0 793 return vmime::make_shared <myTimeoutHandler>();
ferencd@0 794 }
ferencd@0 795 };
ferencd@0 796 \end{lstlisting}
ferencd@0 797
ferencd@0 798 Then, call the {\vcode setTimeoutHandlerFactory()} method on the service object
ferencd@0 799 to set the timeout handler factory to use during the session:
ferencd@0 800
ferencd@0 801 \begin{lstlisting}
ferencd@0 802 theService->setTimeoutHandlerFactory(vmime::make_shared <myTimeoutHandlerFactory>());
ferencd@0 803 \end{lstlisting}
ferencd@0 804
ferencd@0 805
ferencd@0 806 % ============================================================================
ferencd@0 807 \newpage
ferencd@0 808 \section{Secured connection using TLS/SSL}
ferencd@0 809
ferencd@0 810 \subsection{Introduction} % --------------------------------------------------
ferencd@0 811
ferencd@0 812 If you have enabled TLS support in VMime, you can configure messaging services
ferencd@0 813 so that they use a secured connection.
ferencd@0 814
ferencd@0 815 Quoting from RFC-2246 - the TLS 1.0 protocol specification: \emph{`` The TLS
ferencd@0 816 protocol provides communications privacy over the Internet. The protocol
ferencd@0 817 allows client/server applications to communicate in a way that is designed
ferencd@0 818 to prevent eavesdropping, tampering, or message forgery.''}
ferencd@0 819
ferencd@0 820 TLS has the following advantages:
ferencd@0 821
ferencd@0 822 \begin{itemize}
ferencd@0 823 \item authentication: server identity can be verified;
ferencd@0 824 \item privacy: transmission of data between client and server cannot be read
ferencd@0 825 by someone in the middle of the connection;
ferencd@0 826 \item integrity: original data which is transferred between a client and a
ferencd@0 827 server can not be modified by an attacker without being detected.
ferencd@0 828 \end{itemize}
ferencd@0 829
ferencd@0 830 \vnote{What is the difference between SSL and TLS? SSL is a protocol designed
ferencd@0 831 by Netscape. TLS is a standard protocol, and is partly based on version 3 of
ferencd@0 832 the SSL protocol. The two protocols are not interoperable, but TLS does
ferencd@0 833 support a mechanism to back down to SSL 3.}
ferencd@0 834
ferencd@0 835 VMime offers two possibilities for using a secured connection:
ferencd@0 836
ferencd@0 837 \begin{itemize}
ferencd@0 838 \item you can connect to a server listening on a special port (eg. IMAPS
ferencd@0 839 instead of IMAP): this is the classical use of SSL, but is now deprecated;
ferencd@0 840 \item connect to a server listening on the default port, and then begin a
ferencd@0 841 secured connection: this is STARTTLS.
ferencd@0 842 \end{itemize}
ferencd@0 843
ferencd@0 844
ferencd@0 845 \subsection{Setting up a secured connection} % -------------------------------
ferencd@0 846
ferencd@0 847 \subsubsection{Connecting to a ``secured'' port} % ...........................
ferencd@0 848
ferencd@0 849 To use the classical SSL/TLS way, simply use the ``S'' version of the protocol
ferencd@0 850 to connect to the server (eg. \emph{imaps} instead of \emph{imap}). This is
ferencd@0 851 currently available for SMTP, POP3 and IMAP.
ferencd@0 852
ferencd@0 853 \begin{lstlisting}
ferencd@0 854 vmime::shared_ptr <vmime::net::store> store =
ferencd@0 855 theSession->getStore(vmime::utility::url("imaps://example.org"));
ferencd@0 856 \end{lstlisting}
ferencd@0 857
ferencd@0 858 \subsubsection{Using STARTTLS} % .............................................
ferencd@0 859
ferencd@0 860 To make the service start a secured session using the STARTTLS method, simply
ferencd@0 861 set the \emph{connection.tls} property:
ferencd@0 862
ferencd@0 863 \begin{lstlisting}
ferencd@0 864 theService->setProperty("connection.tls", true);
ferencd@0 865 \end{lstlisting}
ferencd@0 866
ferencd@0 867 \vnote{If, for some reason, a secured connection cannot be started, the
ferencd@0 868 default behaviour is to fallback on a normal connection. To make
ferencd@0 869 {\vcode connect()} fail if STARTTLS fails, set the
ferencd@0 870 \emph{connection.tls.required} to \emph{true}.}
ferencd@0 871
ferencd@0 872 \subsection{Certificate verification} % --------------------------------------
ferencd@0 873
ferencd@0 874 \subsubsection{How it works} % ...............................................
ferencd@0 875
ferencd@0 876 If you tried the previous examples, a
ferencd@0 877 {\vcode certificateException} might have been thrown.
ferencd@0 878 This is because the default certificate verifier in VMime did not manage to
ferencd@0 879 verify the certificate, and so could not trust it.
ferencd@0 880
ferencd@0 881 Basically, when you connect to a server using TLS, the server responds with
ferencd@0 882 a list of certificates, called a certificate chain (usually, certificates are
ferencd@0 883 of type X.509\footnote{And VMime currently supports only X.509 certificates}).
ferencd@0 884 The certificate chain is ordered so that the first certificate is the subject
ferencd@0 885 certificate, the second is the subject's issuer one, the third is the issuer's
ferencd@0 886 issuer, and so on.
ferencd@0 887
ferencd@0 888 To decide whether the server can be trusted or not, you have to verify that
ferencd@0 889 \emph{each} certificate is valid (ie. is trusted). For more information
ferencd@0 890 about X.509 and certificate verification, see related articles on Wikipedia
ferencd@0 891 \footnote{See \url{http://wikipedia.org/wiki/Public\_key\_certificate}}.
ferencd@0 892
ferencd@0 893 \subsubsection{Using the default certificate verifier} % .....................
ferencd@0 894
ferencd@0 895 The default certificate verifier maintains a list of root (CAs) and user
ferencd@0 896 certificates that are trusted. By default, the list is empty. So, you have
ferencd@0 897 to initialize it before using the verifier.
ferencd@0 898
ferencd@0 899 The algorithm\footnote{See
ferencd@0 900 \url{http://wikipedia.org/wiki/Certification\_path\_validation\_algorithm}}
ferencd@0 901 used is quite simple:
ferencd@0 902
ferencd@0 903 \begin{enumerate}
ferencd@0 904 \item for every certificate in the chain, verify that the certificate has been
ferencd@0 905 issued by the next certificate in the chain;
ferencd@0 906 \item for every certificate in the chain, verify that the certificate is valid
ferencd@0 907 at the current time;
ferencd@0 908 \item ensure that the first certificate's subject name matches the hostname
ferencd@0 909 of the server;
ferencd@0 910 \item decide whether the subject's certificate can be trusted:
ferencd@0 911 \begin{itemize}
ferencd@0 912 \item first, verify that the the last certificate in the chain was
ferencd@0 913 issued by a third-party that we trust (root CAs);
ferencd@0 914 \item if the issuer certificate cannot be verified against root CAs,
ferencd@0 915 compare the subject's certificate against the trusted certificates
ferencd@0 916 (the certificates the user has decided to trust).
ferencd@0 917 \end{itemize}
ferencd@0 918 \end{enumerate}
ferencd@0 919
ferencd@0 920 First, we need some code to load existing X.509 certificates:
ferencd@0 921
ferencd@0 922 \begin{lstlisting}[caption={Reading a X.509 certificate from a file}]
ferencd@0 923 vmime::shared_ptr <vmime::security::cert::X509Certificate>
ferencd@0 924 loadX509CertificateFromFile(const std::string& path)
ferencd@0 925 {
ferencd@0 926 std::ifstream certFile;
ferencd@0 927 certFile.open(path.c_str(), std::ios::in | std::ios::binary);
ferencd@0 928
ferencd@0 929 if (!certFile)
ferencd@0 930 {
ferencd@0 931 // ...handle error...
ferencd@0 932 }
ferencd@0 933
ferencd@0 934 vmime::utility::inputStreamAdapter is(certFile);
ferencd@0 935 vmime::shared_ptr <vmime::security::cert::X509Certificate> cert;
ferencd@0 936
ferencd@0 937 // Try DER format
ferencd@0 938 cert = vmime::security::cert::X509Certificate::import
ferencd@0 939 (is, vmime::security::cert::X509Certificate::FORMAT_DER);
ferencd@0 940
ferencd@0 941 if (cert != NULL)
ferencd@0 942 return cert;
ferencd@0 943
ferencd@0 944 // Try PEM format
ferencd@0 945 is.reset();
ferencd@0 946 cert = vmime::security::cert::X509Certificate::import
ferencd@0 947 (is, vmime::security::cert::X509Certificate::FORMAT_PEM);
ferencd@0 948
ferencd@0 949 return cert;
ferencd@0 950 }
ferencd@0 951 \end{lstlisting}
ferencd@0 952
ferencd@0 953 Then, we can use the {\vcode loadX509CertificateFromFile} function to load
ferencd@0 954 certificates and initialize the certificate verifier:
ferencd@0 955
ferencd@0 956 \begin{lstlisting}[caption={Using the default certificate verifier}]
ferencd@0 957 vmime::shared_ptr <vmime::security::cert::defaultCertificateVerifier> vrf =
ferencd@0 958 vmime::make_shared <vmime::security::cert::defaultCertificateVerifier>();
ferencd@0 959
ferencd@0 960 // Load root CAs (such as Verisign or Thawte)
ferencd@0 961 std::vector <vmime::shared_ptr <vmime::security::cert::X509Certificate> > rootCAs;
ferencd@0 962
ferencd@0 963 rootCAs.push_back(loadX509CertificateFromFile("/path/to/root-ca1.cer");
ferencd@0 964 rootCAs.push_back(loadX509CertificateFromFile("/path/to/root-ca2.cer");
ferencd@0 965 rootCAs.push_back(loadX509CertificateFromFile("/path/to/root-ca3.cer");
ferencd@0 966
ferencd@0 967 vrf->setX509RootCAs(rootCAs);
ferencd@0 968
ferencd@0 969 // Then, load certificates that the user explicitely chose to trust
ferencd@0 970 std::vector <vmime::shared_ptr <vmime::security::cert::X509Certificate> > trusted;
ferencd@0 971
ferencd@0 972 trusted.push_back(loadX509CertificateFromFile("/path/to/trusted-site1.cer");
ferencd@0 973 trusted.push_back(loadX509CertificateFromFile("/path/to/trusted-site2.cer");
ferencd@0 974
ferencd@0 975 vrf->setX509TrustedCerts(trusted);
ferencd@0 976 \end{lstlisting}
ferencd@0 977
ferencd@0 978
ferencd@0 979 \subsubsection{Writing your own certificate verifier} % ......................
ferencd@0 980
ferencd@0 981 If you need to do more complex verifications on certificates, you will have to
ferencd@0 982 write your own verifier. Your verifier should inherit from the
ferencd@0 983 {\vcode vmime::security::cert::certificateVerifier} class and implement the
ferencd@0 984 method {\vcode verify()}. Then, if the specified certificate chain is trusted,
ferencd@0 985 simply return from the function, or else throw a
ferencd@0 986 {\vcode certificateException}.
ferencd@0 987
ferencd@0 988 The following example shows how to implement an interactive certificate
ferencd@0 989 verifier which relies on the user's decision, and nothing else (you SHOULD NOT
ferencd@0 990 use this in a production application as this is obviously a serious security
ferencd@0 991 issue):
ferencd@0 992
ferencd@0 993 \begin{lstlisting}[caption={A custom certificate verifier}]
ferencd@0 994 class myCertVerifier : public vmime::security::cert::certificateVerifier
ferencd@0 995 {
ferencd@0 996 public:
ferencd@0 997
ferencd@0 998 void verify(vmime::shared_ptr <certificateChain> certs)
ferencd@0 999 {
ferencd@0 1000 // Obtain the subject's certificate
ferencd@0 1001 vmime::shared_ptr <vmime::security::cert::certificate> cert = chain->getAt(0);
ferencd@0 1002
ferencd@0 1003 std::cout << std::endl;
ferencd@0 1004 std::cout << "Server sent a '" << cert->getType() << "'"
ferencd@0 1005 << " certificate." << std::endl;
ferencd@0 1006 std::cout << "Do you want to accept this certificate? (Y/n) ";
ferencd@0 1007 std::cout.flush();
ferencd@0 1008
ferencd@0 1009 std::string answer;
ferencd@0 1010 std::getline(std::cin, answer);
ferencd@0 1011
ferencd@0 1012 if (answer.length() != 0 && (answer[0] == 'Y' || answer[0] == 'y'))
ferencd@0 1013 return; // OK, we trust the certificate
ferencd@0 1014
ferencd@0 1015 // Don't trust this certificate
ferencd@0 1016 throw vmime::security::cert::certificateException();
ferencd@0 1017 }
ferencd@0 1018 };
ferencd@0 1019 \end{lstlisting}
ferencd@0 1020
ferencd@0 1021 \vnote{In production code, it may be a good idea to remember user's decisions
ferencd@0 1022 about which certificates to trust and which not. See {\vexample Example6} for
ferencd@0 1023 a basic cache implementation.}
ferencd@0 1024
ferencd@0 1025 Finally, to make the service use your own certificate verifier, simply write:
ferencd@0 1026
ferencd@0 1027 \begin{lstlisting}
ferencd@0 1028 theService->setCertificateVerifier(vmime::make_shared <myCertVerifier>());
ferencd@0 1029 \end{lstlisting}
ferencd@0 1030
ferencd@0 1031 \subsection{SSL/TLS Properties} % --------------------------------------------
ferencd@0 1032
ferencd@0 1033 If you want to customize behavior or set some options on TLS/SSL connection,
ferencd@0 1034 you may use the TLSProperties object, and pass it to the service session. The
ferencd@0 1035 TLS/SSL options must be set {\em before} creating any service with the session
ferencd@0 1036 (ie. before calling either {\vcode getStore()} or {\vcode getTransport()} on
ferencd@0 1037 the session), or they will not be used.
ferencd@0 1038
ferencd@0 1039 The following example shows how to set the cipher suite preferences for TLS:
ferencd@0 1040
ferencd@0 1041 \begin{lstlisting}[caption={Setting TLS cipher suite preferences}]
ferencd@0 1042 vmime::shared_ptr <vmime::net::session> sess = /* ... */;
ferencd@0 1043
ferencd@0 1044 vmime::shared_ptr <vmime::net::tls::TLSProperties> tlsProps =
ferencd@0 1045 vmime::make_shared <vmime::net::tls::TLSProperties>();
ferencd@0 1046
ferencd@0 1047 // for OpenSSL
ferencd@0 1048 tlsProps->setCipherString("HIGH:!ADH:@STRENGTH");
ferencd@0 1049
ferencd@0 1050 // for GNU TLS
ferencd@0 1051 tlsProps->setCipherString("NORMAL:%SSL3_RECORD_VERSION");
ferencd@0 1052
ferencd@0 1053 sess->setTLSProperties(tlsProps);
ferencd@0 1054 \end{lstlisting}
ferencd@0 1055
ferencd@0 1056 Please note that the cipher suite string format and meaning depend on the
ferencd@0 1057 underlying TLS library (either OpenSSL or GNU TLS):
ferencd@0 1058
ferencd@0 1059 \begin{itemize}
ferencd@0 1060 \item for GNU TLS, read this: \newline
ferencd@0 1061 \url{http://gnutls.org/manual/html\_node/Priority-Strings.html}
ferencd@0 1062
ferencd@0 1063 \item for OpenSSL, read this: \newline
ferencd@0 1064 \url{http://www.openssl.org/docs/apps/ciphers.html#CIPHER\_STRINGS}
ferencd@0 1065 \end{itemize}
ferencd@0 1066
ferencd@0 1067 You may also set cipher suite preferences using predefined constants that
ferencd@0 1068 map to generic security modes:
ferencd@0 1069
ferencd@0 1070 \begin{lstlisting}[caption={Setting TLS cipher suite preferences using predefined modes}]
ferencd@0 1071 sess->setCipherSuite(vmime::net::tls::TLSProperties::CIPHERSUITE_HIGH);
ferencd@0 1072 \end{lstlisting}
ferencd@0 1073
ferencd@0 1074 The following constants are available:
ferencd@0 1075
ferencd@0 1076 \noindent\begin{tabularx}{1.0\textwidth}{|l|X|}
ferencd@0 1077 \hline
ferencd@0 1078 {\bf Constant} &
ferencd@0 1079 {\bf Meaning} \\
ferencd@0 1080 \hline
ferencd@0 1081 CIPHERSUITE\_HIGH &
ferencd@0 1082 High encryption cipher suites ($>$ 128 bits) \\
ferencd@0 1083 \hline
ferencd@0 1084 CIPHERSUITE\_MEDIUM &
ferencd@0 1085 Medium encryption cipher suites ($>=$ 128 bits) \\
ferencd@0 1086 \hline
ferencd@0 1087 CIPHERSUITE\_LOW &
ferencd@0 1088 Low encryption cipher suites ($>=$ 64 bits) \\
ferencd@0 1089 \hline
ferencd@0 1090 CIPHERSUITE\_DEFAULT &
ferencd@0 1091 Default cipher suite (actual cipher suites used depends
ferencd@0 1092 on the underlying SSL/TLS library) \\
ferencd@0 1093 \hline
ferencd@0 1094 \end{tabularx}
ferencd@0 1095