1204 lines
42 KiB
TeX
1204 lines
42 KiB
TeX
\chapter{Working with Messaging Services}
|
|
|
|
% ============================================================================
|
|
\section{Introduction}
|
|
|
|
In addition to parsing and building MIME messages, VMime also offers a lot of
|
|
features to work with messaging services. This includes connecting to remote
|
|
messaging stores (like IMAP or POP3), local stores (maildir) and transport
|
|
services (send messages over SMTP or local sendmail), through an unified
|
|
interface (see Figure \ref{uml_messaging_module}). That means that you can
|
|
use independently IMAP of POP3 without having to change any line of code.
|
|
|
|
Source code of {\vexample Example6} covers all features presented in this
|
|
chapter, so it is important you take some time to read it.
|
|
|
|
\begin{figure}
|
|
\center\includegraphics[width=0.9\textwidth]
|
|
{images/messaging-services.png}\endcenter
|
|
\caption{Overall structure of the messaging module}
|
|
\label{uml_messaging_module}
|
|
\end{figure}
|
|
|
|
The interface is composed of five classes:
|
|
|
|
\begin{itemize}
|
|
\item {\vcode vmime::net::service}: this is the base interface for a
|
|
messaging service. It can be either a store service or a transport
|
|
service.
|
|
|
|
\item {\vcode vmime::net::serviceFactory}: create instances of a service.
|
|
This is used internally by the session object (see below).
|
|
|
|
\item {\vcode vmime::net::store}: interface for a store service. A store
|
|
service offers access to a set of folders containing messages. This is
|
|
used for IMAP, POP3 and maildir.
|
|
|
|
\item {\vcode vmime::net::transport}: interface for a transport service.
|
|
A transport service is capable of sending messages. This is used for
|
|
SMTP and sendmail.
|
|
|
|
\item {\vcode vmime::net::session}: a session object is used to store the
|
|
parameters used by a service (eg. connection parameters). Each service
|
|
instance is associated with only one session. The session object is capable
|
|
of creating instances of services.
|
|
\end{itemize}
|
|
|
|
The following classes are specific to store services:
|
|
|
|
\begin{itemize}
|
|
\item {\vcode vmime::net::folder}: a folder can either contain other folders
|
|
or messages, or both.
|
|
|
|
\item {\vcode vmime::net::message}: this is the interface for dealing with
|
|
messages. For a given message, you can have access to its flags, its MIME
|
|
structure and you can also extract the whole message data or given parts (if
|
|
supported by the underlying protocol).
|
|
\end{itemize}
|
|
|
|
|
|
% ============================================================================
|
|
\section{Working with sessions}
|
|
|
|
\subsection{Setting properties} % --------------------------------------------
|
|
|
|
Sessions are used to store configuration parameters for services. They
|
|
contains a set of typed properties that can modify the behaviour of the
|
|
services. Before using a messaging service, you must create and
|
|
initialize a session object:
|
|
|
|
\begin{lstlisting}
|
|
vmime::shared_ptr <vmime::net::session> theSession = vmime::net::session::create();
|
|
\end{lstlisting}
|
|
|
|
Session properties include:
|
|
|
|
\begin{itemize}
|
|
\item connection parameters: host and port to connect to;
|
|
\item authentication parameters: user credentials required to use the
|
|
service (if any);
|
|
\item protocol-specific parameters: enable or disable extensions (eg. APOP
|
|
support in POP3).
|
|
\end{itemize}
|
|
|
|
Properties are stored using a dotted notation, to specify the service type,
|
|
the protocol name, the category and the name of the property:
|
|
|
|
\begin{verbatim}
|
|
{service_type}.{protocol}.category.name
|
|
\end{verbatim}
|
|
|
|
An example of property is \emph{store.pop3.options.apop} (used to enable or
|
|
disable the use of APOP authentication). The \emph{store.pop3} part is called
|
|
the \emph{prefix}. This allow specifying different values for the same
|
|
property depending on the protocol used.
|
|
|
|
The session properties are stored in a {\vcode vmime::propertySet} object.
|
|
To set the value of a property, you can use either:
|
|
|
|
\begin{lstlisting}
|
|
theSession->getProperties().setProperty("property-name", value);
|
|
\end{lstlisting}
|
|
|
|
or:
|
|
|
|
\begin{lstlisting}
|
|
theSession->getProperties()["property-name"] = value;
|
|
\end{lstlisting}
|
|
|
|
|
|
\subsection{Available properties} % ------------------------------------------
|
|
|
|
Following is a list of available properties and the protocols they apply to,
|
|
as the time of writing this documentation\footnote{You can get an up-to-date
|
|
list of the properties by running \vexample{Example7}}. For better clarity,
|
|
the prefixes do not appear in this table.
|
|
|
|
\begin{table}[!ht]
|
|
\noindent\begin{tabularx}{1.0\textwidth}{|l|c|X|c|c|c|c|c|c|c|c|}
|
|
\hline
|
|
{\bf Property name} &
|
|
{\bf Type} &
|
|
{\bf Description} &
|
|
\verti{\bf POP3} &
|
|
\verti{\bf POP3S} &
|
|
\verti{\bf IMAP} &
|
|
\verti{\bf IMAPS} &
|
|
\verti{\bf SMTP} &
|
|
\verti{\bf SMTPS} &
|
|
\verti{\bf maildir} &
|
|
\verti{\bf sendmail} \\
|
|
\hline
|
|
\hline
|
|
options.sasl & bool & Set to {\vcode true} to use SASL authentication, if
|
|
available. & \vdot & \vdot & \vdot & \vdot & \vdot & \vdot & & \\
|
|
\hline
|
|
options.sasl.fallback & bool & Fail if SASL authentication failed (do not
|
|
try other authentication mechanisms). & \vdot & \vdot & \vdot & \vdot &
|
|
\vdot & \vdot & & \\
|
|
\hline
|
|
auth.username\footnote{You should use authenticators
|
|
instead.\label{fn_auth_username}} & string & Set the username of the account
|
|
to connect to. & \vdot & \vdot & \vdot & \vdot & \vdot & \vdot & & \\
|
|
\hline
|
|
auth.password\footref{fn_auth_username} & string & Set the password of the
|
|
account. & \vdot & \vdot & \vdot & \vdot & \vdot & \vdot & & \\
|
|
\hline
|
|
connection.tls & bool & Set to {\vcode true} to start a secured connection
|
|
using STARTTLS extension, if available. & \vdot & & \vdot & & \vdot & & & \\
|
|
\hline
|
|
connection.tls.required & bool & Fail if a secured connection cannot be
|
|
started. & \vdot & & \vdot & & \vdot & & & \\
|
|
\hline
|
|
server.address & string & Server host name or IP address. &\vdot & \vdot &
|
|
\vdot & \vdot & \vdot & \vdot & & \\
|
|
\hline
|
|
server.port & int & Server port. & \vdot & \vdot & \vdot & \vdot &
|
|
\vdot & \vdot & & \\
|
|
\hline
|
|
server.rootpath & string & Root directory for mail repository (eg.
|
|
\emph{/home/vincent/Mail}). & & & & & & & \vdot & \\
|
|
\hline
|
|
\end{tabularx}
|
|
\caption{Properties common to all protocols}
|
|
\end{table}
|
|
|
|
\newpage
|
|
These are the protocol-specific options:
|
|
|
|
\begin{table}[!ht]
|
|
\noindent\begin{tabularx}{1.0\textwidth}{|l|c|X|}
|
|
\hline
|
|
{\bf Property name} &
|
|
{\bf Type} &
|
|
{\bf Description} \\
|
|
% POP3/POP3S
|
|
\hline
|
|
\multicolumn{3}{|c|}{POP3, POP3S} \\
|
|
\hline
|
|
store.pop3.options.apop & bool & Enable or disable authentication with
|
|
APOP (if SASL is enabled, this occurs after all SASL mechanisms have been
|
|
tried). \\
|
|
\hline
|
|
store.pop3.options.apop.fallback & bool & If set to {\vcode true} and
|
|
APOP fails, the authentication process fails (ie. unsecure plain text
|
|
authentication is not used). \\
|
|
\hline
|
|
% SMTP
|
|
\multicolumn{3}{|c|}{SMTP, SMTPS} \\
|
|
\hline
|
|
transport.smtp.options.need-authentication & bool & Set to \emph{true} if
|
|
the server requires to authenticate before sending messages. \\
|
|
\hline
|
|
transport.smtp.options.pipelining & bool & Set to {\vcode false} to disable
|
|
command pipelining, if the server supports it (default is {\vcode true}). \\
|
|
\hline
|
|
transport.smtp.options.chunking & bool & Set to {\vcode false} to disable
|
|
CHUNKING extension, if the server supports it (default is {\vcode true}). \\
|
|
\hline
|
|
% sendmail
|
|
\multicolumn{3}{|c|}{sendmail} \\
|
|
\hline
|
|
transport.sendmail.binpath & string & The path to the \emph{sendmail}
|
|
executable on your system. The default is the one found by the configuration
|
|
script when VMime was built. \\
|
|
\hline
|
|
\end{tabularx}
|
|
\caption{Protocol-specific options}
|
|
\end{table}
|
|
|
|
|
|
\subsection{Instanciating services} % ----------------------------------------
|
|
|
|
You can create a service either by specifying its protocol name, or by
|
|
specifying the URL of the service. Creation by name is deprecated so
|
|
this chapter only presents the latter option.
|
|
|
|
The URL scheme for connecting to services is:
|
|
|
|
\begin{verbatim}
|
|
protocol://[username[:password]@]host[:port]/[root-path]
|
|
\end{verbatim}
|
|
|
|
\vnote{For local services (ie. \emph{sendmail} and \emph{maildir}), the host
|
|
part is not used, but it must not be empty (you can use "localhost").}
|
|
|
|
The following table shows an example URL for each service:
|
|
|
|
\noindent\begin{tabularx}{1.0\textwidth}{|c|X|}
|
|
\hline
|
|
{\bf Service} &
|
|
{\bf Connection URL} \\
|
|
\hline
|
|
imap, imaps & {\tt imap://imap.example.com},
|
|
{\tt imaps://vincent:pass@example.com} \\
|
|
\hline
|
|
pop3, pop3s & {\tt pop3://pop3.example.com} \\
|
|
\hline
|
|
smtp, smtps & {\tt smtp://smtp.example.com} \\
|
|
\hline
|
|
maildir & {\tt maildir://localhost/home/vincent/Mail} (host not used) \\
|
|
\hline
|
|
sendmail & {\tt sendmail://localhost} (host not used, always localhost) \\
|
|
\hline
|
|
\end{tabularx}
|
|
|
|
\newpage
|
|
|
|
When you have the connection URL, instanciating the service is quite simple.
|
|
Depending on the type of service, you will use either {\vcode getStore()} or
|
|
{\vcode getTransport()}. For example, for store services, use:
|
|
|
|
\begin{lstlisting}
|
|
vmime::utility:url url("imap://user:pass@imap.example.com");
|
|
vmime::shared_ptr <vmime::net::store> st = sess->getStore(url);
|
|
\end{lstlisting}
|
|
|
|
and for transport services:
|
|
|
|
\begin{lstlisting}
|
|
vmime::utility:url url("smtp://smtp.example.com");
|
|
vmime::shared_ptr <vmime::net::transport> tr = sess->getTransport(url);
|
|
\end{lstlisting}
|
|
|
|
|
|
% ============================================================================
|
|
\section{User credentials and authenticators}
|
|
|
|
Some services need some user credentials (eg. username and password) to open
|
|
a session. In VMime, user credentials can be specified in the session
|
|
properties or by using a custom authenticator (callback).
|
|
|
|
\begin{lstlisting}[caption={Setting user credentials using session
|
|
properties}]
|
|
vmime::shared_ptr <vmime::net::session> sess; // Suppose we have a session
|
|
|
|
sess->getProperties()["store.imap.auth.username"] = "vincent";
|
|
sess->getProperties()["store.imap.auth.password"] = "my-password";
|
|
\end{lstlisting}
|
|
|
|
Although not recommended, you can also specify username and password
|
|
directly in the connection URL,
|
|
ie: \emph{imap://username:password@imap.example.com/}. This works only for
|
|
services requiring an username and a password as user credentials, and no
|
|
other information.
|
|
|
|
Sometimes, it may not be very convenient to set username/password in the
|
|
session properties, or not possible (eg. extended SASL mechanisms) . That's
|
|
why VMime offers an alternate way of getting user credentials: the
|
|
{\vcode authenticator} object. Basically, an authenticator is an object that
|
|
can return user credentials on-demand (like a callback).
|
|
|
|
Currently, there are two types of authenticator in VMime: a basic
|
|
authenticator (class {\vcode vmime::security::authenticator}) and, if SASL
|
|
support is enabled, a SASL authenticator
|
|
(class {\vcode vmime::security::sasl::SASLAuthenticator}). Usually, you
|
|
should use the default implementations, or at least make your own
|
|
implementation inherit from them.
|
|
|
|
The following example shows how to use a custom authenticator to request
|
|
the user to enter her/his credentials:
|
|
|
|
\begin{lstlisting}[caption={A simple interactive authenticator}]
|
|
class myAuthenticator : public vmime::security::defaultAuthenticator {
|
|
|
|
const string getUsername() const {
|
|
|
|
std::cout << "Enter your username: " << std::endl;
|
|
|
|
vmime::string res;
|
|
std::getline(std::cin, res);
|
|
|
|
return res;
|
|
}
|
|
|
|
const string getPassword() const {
|
|
|
|
std::cout << "Enter your password: " << std::endl;
|
|
|
|
vmime::string res;
|
|
std::getline(std::cin, res);
|
|
|
|
return res;
|
|
}
|
|
};
|
|
\end{lstlisting}
|
|
|
|
This is how to use it:
|
|
|
|
\begin{lstlisting}
|
|
// First, create a session
|
|
vmime::shared_ptr <vmime::net::session> sess = vmime::net::session::create();
|
|
|
|
// Next, initialize a service which will use our authenticator
|
|
vmime::shared_ptr <vmime::net::store> st = sess->getStore(
|
|
vmime::utility::url("imap://imap.example.com"),
|
|
/* use our authenticator */ vmime::make_shared <myAuthenticator>()
|
|
);
|
|
\end{lstlisting}
|
|
|
|
\vnote{An authenticator object should be used with one and only one service
|
|
at a time. This is required because the authentication process may need to
|
|
retrieve the service name (SASL).}
|
|
|
|
Of course, this example is quite simplified. For example, if several
|
|
authentication mechanisms are tried, the user may be requested to enter the
|
|
same information multiple times. See {\vexample Example6} for a more complex
|
|
implementation of an authenticator, with caching support.
|
|
|
|
If you want to use SASL (ie. if \emph{options.sasl} is set to \emph{true}),
|
|
your authenticator must inherit from
|
|
{\vcode vmime::security::sasl::SASLAuthenticator} or
|
|
{\vcode vmime::security::sasl::defaultSASLAuthenticator}, even if you do not
|
|
use the SASL-specific methods {\vcode getAcceptableMechanisms()} and
|
|
{\vcode setSASLMechanism()}. Have a look at {\vexample Example6} to see an
|
|
implementation of an SASL authenticator.
|
|
|
|
\begin{lstlisting}[caption={A simple SASL authenticator}]
|
|
class mySASLAuthenticator : public vmime::security::sasl::defaultSASLAuthenticator {
|
|
|
|
typedef vmime::security::sasl::SASLMechanism mechanism; // save us typing
|
|
|
|
const std::vector <vmime::shared_ptr <mechanism> > getAcceptableMechanisms(
|
|
const std::vector <vmime::shared_ptr <mechanism> >& available,
|
|
const vmime::shared_ptr <mechanism>& suggested
|
|
) const {
|
|
|
|
// Here, you can sort the SASL mechanisms in the order they will be
|
|
// tried. If no SASL mechanism is acceptable (ie. for example, not
|
|
// enough secure), you can return an empty list.
|
|
//
|
|
// If you do not want to bother with this, you can simply return
|
|
// the default list, which is ordered by security strength.
|
|
return defaultSASLAuthenticator::
|
|
getAcceptableMechanisms(available, suggested);
|
|
}
|
|
|
|
void setSASLMechanism(const vmime::shared_ptr <mechanism>& mech) {
|
|
|
|
// This is called when the authentication process is going to
|
|
// try the specified mechanism.
|
|
//
|
|
// The mechanism name is in mech->getName()
|
|
|
|
defaultSASLAuthenticator::setSASLMechanism(mech);
|
|
}
|
|
|
|
// ...implement getUsername() and getPassword()...
|
|
};
|
|
\end{lstlisting}
|
|
|
|
|
|
% ============================================================================
|
|
\section{Using transport service}
|
|
|
|
You have two possibilities for giving message data to the service when you
|
|
want to send a message:
|
|
|
|
\begin{itemize}
|
|
\item either you have a reference to a message (type {\vcode vmime::message})
|
|
and you can simply call {\vcode send(msg)};
|
|
\item or you only have raw message data (as a string, for example), and you
|
|
have to call the second overload of {\vcode send()}, which takes additional
|
|
parameters (corresponding to message envelope);
|
|
\end{itemize}
|
|
|
|
The following example illustrates the use of a transport service to send a
|
|
message using the second method:
|
|
|
|
\begin{lstlisting}[caption={Using a transport service}]
|
|
const vmime::string msgData =
|
|
"From: me@example.org \r\n"
|
|
"To: you@example.org \r\n"
|
|
"Date: Sun, Oct 30 2005 17:06:42 +0200 \r\n"
|
|
"Subject: Test \r\n"
|
|
"\r\n"
|
|
"Message body";
|
|
|
|
// Create a new session
|
|
vmime::utility::url url("smtp://example.com");
|
|
|
|
vmime::shared_ptr <vmime::net::session> sess = vmime::net::session::create();
|
|
|
|
// Create an instance of the transport service
|
|
vmime::shared_ptr <vmime::net::transport> tr = sess->getTransport(url);
|
|
|
|
// Connect it
|
|
tr->connect();
|
|
|
|
// Send the message
|
|
vmime::utility::inputStreamStringAdapter is(msgData);
|
|
|
|
vmime::mailbox from("me@example.org");
|
|
vmime::mailboxList to;
|
|
to.appendMailbox(vmime::make_shared <vmime::mailbox>("you@example.org"));
|
|
|
|
tr->send(
|
|
/* expeditor */ from,
|
|
/* recipient(s) */ to,
|
|
/* data */ is,
|
|
/* total length */ msgData.length()
|
|
);
|
|
|
|
// We have finished using the service
|
|
tr->disconnect();
|
|
\end{lstlisting}
|
|
|
|
\vnote{Exceptions can be thrown at any time when using a service. For better
|
|
clarity, exceptions are not caught here, but be sure to catch them in your own
|
|
application to provide error feedback to the user.}
|
|
|
|
If you use SMTP, you can enable authentication by setting some properties
|
|
on the session object ({\vcode service::setProperty()} is a shortcut for
|
|
setting properties on the session with the correct prefix):
|
|
|
|
\begin{lstlisting}
|
|
tr->setProperty("options.need-authentication", true);
|
|
tr->setProperty("auth.username", "user");
|
|
tr->setProperty("auth.password", "password");
|
|
\end{lstlisting}
|
|
|
|
|
|
% ============================================================================
|
|
\section{Using store service}
|
|
|
|
\subsection{Connecting to a store} % -----------------------------------------
|
|
|
|
The first basic step for using a store service is to connect to it. The
|
|
following example shows how to initialize a session and instanciate the
|
|
store service:
|
|
|
|
\begin{lstlisting}[caption={Connecting to a store service}]
|
|
// Create a new session
|
|
vmime::utility::url url("imap://vincent:password@imap:example.org");
|
|
|
|
vmime::shared_ptr <vmime::net::session> sess = vmime::net::session::create();
|
|
|
|
// Create an instance of the transport service
|
|
vmime::shared_ptr <vmime::net::store> store = sess->getStore(url);
|
|
|
|
// Connect it
|
|
store->connect();
|
|
\end{lstlisting}
|
|
|
|
\vnote{{\vexample Example6} contains a more complete example for connecting
|
|
to a store service, with support for a custom authenticator.}
|
|
|
|
\subsection{Opening a folder} % ----------------------------------------------
|
|
|
|
You can open a folder using two different access modes: either in
|
|
\emph{read-only} mode (where you can only read message flags and contents), or
|
|
in \emph{read-write} mode (where you can read messages, but also delete them
|
|
or add new ones). When you have a reference to a folder, simply call the
|
|
{\vcode open()} method with the desired access mode:
|
|
|
|
\begin{lstlisting}
|
|
folder->open(vmime::net::folder::MODE_READ_WRITE);
|
|
\end{lstlisting}
|
|
|
|
\vnote{Not all stores support the \emph{read-write} mode. By default, if the
|
|
\emph{read-write} mode is not available, the folder silently fall backs on
|
|
the \emph{read-only} mode, unless the \emph{failIfModeIsNotAvailable} argument
|
|
to {\vcode open()} is set to true.}
|
|
|
|
Call {\vcode getDefaultFolder()} on the store to obtain a reference to the
|
|
default folder, which is usually the INBOX folder (where messages arrive when
|
|
they are received).
|
|
|
|
You can also open a specific folder by specifying its path. The following
|
|
example will open a folder named \emph{bar}, which is a child of \emph{foo}
|
|
in the root folder:
|
|
|
|
\begin{lstlisting}[caption={Opening a folder from its path}]
|
|
vmime::net::folder::path path;
|
|
path /= vmime::net::folder::path::component("foo");
|
|
path /= vmime::net::folder::path::component("bar");
|
|
|
|
vmime::shared_ptr <vmime::net::folder> fld = store->getFolder(path);
|
|
fld->open(vmime::net::folder::MODE_READ_WRITE);
|
|
\end{lstlisting}
|
|
|
|
\vnote{You can specify a path as a string as there is no way to get the
|
|
separator used to delimitate path components. Always use {\vcode operator/=}
|
|
or {\vcode appendComponent}.}
|
|
|
|
\vnote{Path components are of type {\vcode vmime::word}, which means that
|
|
VMime supports folder names with extended characters, not only 7-bit
|
|
US-ASCII. However, be careful that this may not be supported by the
|
|
underlying store protocol (IMAP supports it, because it uses internally a
|
|
modified UTF-7 encoding).}
|
|
|
|
\subsection{Fetching messages} % ---------------------------------------------
|
|
|
|
You can fetch some information about a message without having to download the
|
|
whole message. Moreover, folders support fetching for multiple messages in
|
|
a single request, for better performance. The following items are currently
|
|
available for fetching:
|
|
|
|
\begin{itemize}
|
|
\item {\bf envelope}: sender, recipients, date and subject;
|
|
\item {\bf structure}: MIME structure of the message;
|
|
\item {\bf content-info}: content-type of the root part;
|
|
\item {\bf flags}: message flags;
|
|
\item {\bf size}: message size;
|
|
\item {\bf header}: retrieve all the header fields of a message;
|
|
\item {\bf uid}: unique identifier of a message;
|
|
\item {\bf importance}: fetch header fields suitable for use with
|
|
{\vcode misc::importanceHelper}.
|
|
\end{itemize}
|
|
|
|
\vnote{Not all services support all fetchable items. Call
|
|
{\vcode getFetchCapabilities()} on a folder to know which information can be
|
|
fetched by a service.}
|
|
|
|
The following code shows how to list all the messages in a folder, and
|
|
retrieve basic information to show them to the user:
|
|
|
|
\begin{lstlisting}[caption={Fetching information about multiple messages}]
|
|
std::vector <ref <vmime::net::message> > allMessages =
|
|
folder->getMessages(vmime::net::messageSet::byNumber(1, -1));
|
|
// -1 is a special value to mean "the number of the last message in the folder"
|
|
|
|
folder->fetchMessages(
|
|
allMessages,
|
|
vmime::net::fetchAttributes::FLAGS |
|
|
vmime::net::fetchAttributes::ENVELOPE
|
|
);
|
|
|
|
for (unsigned int i = 0 ; i < allMessages.size() ; ++i) {
|
|
|
|
vmime::shared_ptr <vmime::net::message> msg = allMessages[i];
|
|
|
|
const int flags = msg->getFlags();
|
|
|
|
std::cout << "Message " << i << ":" << std::endl;
|
|
|
|
if (flags & vmime::net::message::FLAG_SEEN) {
|
|
std::cout << " - is read" << std::endl;
|
|
}
|
|
if (flags & vmime::net::message::FLAG_DELETED) {
|
|
std::cout << " - is deleted" << std::endl;
|
|
}
|
|
|
|
vmime::shared_ptr <const vmime::header> hdr = msg->getHeader();
|
|
|
|
std::cout << " - sent on " << hdr->Date()->generate() << std::endl;
|
|
std::cout << " - sent by " << hdr->From()->generate() << std::endl;
|
|
}
|
|
\end{lstlisting}
|
|
|
|
IMAP supports fetching specific header fields of a message. Here is how to use
|
|
the {\vcode fetchAttributes} object to do it:
|
|
|
|
\begin{lstlisting}[caption={Using fetchAttributes object to fetch specific header fields of a message}]
|
|
|
|
// Fetch message flags and the "Received" and "X-Mailer" header fields
|
|
vmime::net::fetchAttributes fetchAttribs;
|
|
fetchAttribs.add(vmime::net::fetchAttributes::FLAGS);
|
|
fetchAttribs.add("Received");
|
|
fetchAttribs.add("X-Mailer");
|
|
|
|
folder->fetchMessages(allMessages, fetchAttribs);
|
|
\end{lstlisting}
|
|
|
|
|
|
\subsection{Extracting messages and parts}
|
|
|
|
To extract the whole contents of a message (including headers), use the
|
|
{\vcode extract()} method on a {\vcode vmime::net::message} object. The
|
|
following example extracts the first message in the default folder:
|
|
|
|
\begin{lstlisting}[caption={Extracting messages}]
|
|
// Get a reference to the folder and to its first message
|
|
vmime::shared_ptr <vmime::net::folder> folder = store->getDefaultFolder();
|
|
vmime::shared_ptr <vmime::net::message> msg = folder->getMessage(1);
|
|
|
|
// Write the message contents to the standard output
|
|
vmime::utility::outputStreamAdapter out(std::cout);
|
|
msg->extract(out);
|
|
\end{lstlisting}
|
|
|
|
Some protocols (like IMAP) also support the extraction of specific MIME parts
|
|
of a message without downloading the whole message. This can save bandwidth
|
|
and time. The method {\vcode extractPart()} is used in this case:
|
|
|
|
\begin{lstlisting}[caption={Extracting a specific MIME part of a message}]
|
|
// Fetching structure is required before extracting a part
|
|
folder->fetchMessage(msg, vmime::net::fetchAttributes::STRUCTURE);
|
|
|
|
// Now, we can extract the part
|
|
msg->extractPart(msg->getStructure()->getPartAt(0)->getPartAt(1));
|
|
\end{lstlisting}
|
|
|
|
Suppose we have a message with the following structure:
|
|
|
|
\begin{verbatim}
|
|
multipart/mixed
|
|
text/html
|
|
image/jpeg [*]
|
|
\end{verbatim}
|
|
|
|
The previous example will extract the header and body of the \emph{image/jpeg}
|
|
part.
|
|
|
|
\subsection{Deleting messages} % ---------------------------------------------
|
|
|
|
The following example will delete the second and the third message from the
|
|
store.
|
|
|
|
\begin{lstlisting}[caption={Deleting messages}]
|
|
vmime::shared_ptr <vmime::net::folder> folder = store->getDefaultFolder();
|
|
|
|
folder->deleteMessages(vmime::net::messageSet::byNumber(/* from */ 2, /* to */ 3));
|
|
|
|
// This is equivalent
|
|
std::vector <int> nums;
|
|
nums.push_back(2);
|
|
nums.push_back(3);
|
|
folder->deleteMessages(vmime::net::messageSet::byNumber(nums));
|
|
|
|
// This is also equivalent (but will require 2 roundtrips to server)
|
|
folder->deleteMessages(vmime::net::messageSet::byNumber(2));
|
|
folder->deleteMessages(vmime::net::messageSet::byNumber(2)); // renumbered, 3 becomes 2
|
|
\end{lstlisting}
|
|
|
|
\subsection{Events} % --------------------------------------------------------
|
|
|
|
As a result of executing some operation (or from time to time, even if no
|
|
operation has been performed), a store service can send events to notify you
|
|
that something has changed (eg. the number of messages in a folder). These
|
|
events may allow you to update the user interface associated to a message
|
|
store.
|
|
|
|
Currently, there are three types of event:
|
|
|
|
\begin{itemize}
|
|
\item {\bf message change}: sent when the number of messages in a folder
|
|
has changed (ie. some messages have been added or removed);
|
|
\item {\bf message count change}: sent when one or more message(s) have
|
|
changed (eg. flags or deleted status);
|
|
\item {\bf folder change}: sent when a folder has been created, renamed or
|
|
deleted.
|
|
\end{itemize}
|
|
|
|
You can register a listener for each event type by using the corresponding
|
|
methods on a {\vcode folder} object: {\vcode addMessageChangedListener()},
|
|
{\vcode addMessageCountListener()} or {\vcode addFolderListener()}. For more
|
|
information, please read the class documentation for
|
|
{\vcode vmime::net::events} namespace.
|
|
|
|
|
|
% ============================================================================
|
|
\section{Handling timeouts}
|
|
|
|
Unexpected errors can occur while messaging services are performing
|
|
operations and waiting a response from the server (eg. server stops
|
|
responding, network link falls down). As all operations as synchronous,
|
|
they can be ``blocked'' a long time before returning (in fact, they loop
|
|
until they either receive a response from the server, or the underlying
|
|
socket system returns an error).
|
|
|
|
VMime provides a mechanism to control the duration of operations. This
|
|
mechanism allows the program to cancel an operation that is currently
|
|
running.
|
|
|
|
An interface called {\vcode timeoutHandler} is provided:
|
|
|
|
\begin{lstlisting}
|
|
class timeoutHandler : public object {
|
|
|
|
/** Called to test if the time limit has been reached.
|
|
*
|
|
* @return true if the timeout delay is elapsed
|
|
*/
|
|
virtual const bool isTimeOut() = 0;
|
|
|
|
/** Called to reset the timeout counter.
|
|
*/
|
|
virtual void resetTimeOut() = 0;
|
|
|
|
/** Called when the time limit has been reached (when
|
|
* isTimeOut() returned true).
|
|
*
|
|
* @return true to continue (and reset the timeout)
|
|
* or false to cancel the current operation
|
|
*/
|
|
virtual const bool handleTimeOut() = 0;
|
|
};
|
|
\end{lstlisting}
|
|
|
|
While the operation runs, the service calls {\vcode isTimeout()} at variable
|
|
intervals. If the {\vcode isTimeout()} function returns {\vcode true},
|
|
then {\vcode handleTimeout()} is called. If the {\vcode handleTimeout()}
|
|
function returns {\vcode false}, the operation is cancelled and
|
|
an {\vcode operation\_timed\_out} exception is thrown. Else, if
|
|
{\vcode handleTimeout()} returns true, the operation continues and the
|
|
timeout counter is reset.
|
|
The function {\vcode resetTimeout()} is called each time data has
|
|
been received from the server to reset the timeout delay.
|
|
|
|
When using a service, a default timeout handler is set: if an operation
|
|
is blocked for more than 30 seconds (ie. network link is down and no data
|
|
was received since 30 seconds), an {\vcode operation\_timed\_out} exception
|
|
is thrown.
|
|
|
|
The following example shows how to implement a simple timeout handler:
|
|
|
|
\begin{lstlisting}[caption={Implementing a simple timeout handler}]
|
|
class myTimeoutHandler : public vmime::net::timeoutHandler {
|
|
|
|
public:
|
|
|
|
myTimeoutHandler() {
|
|
|
|
m_startTime = time(NULL);
|
|
}
|
|
|
|
const bool isTimeOut() {
|
|
|
|
return time(NULL) >= m_startTime + 30; // 30 seconds timeout
|
|
}
|
|
|
|
void resetTimeOut() {
|
|
|
|
m_startTime = time(NULL);
|
|
}
|
|
|
|
const bool handleTimeOut() {
|
|
|
|
std::cout << "Operation timed out." << std::endl;
|
|
<< "Press [Y] to continue, or [N] to "
|
|
<< "cancel the operation." << std::endl;
|
|
|
|
std::string response;
|
|
std::cin >> response;
|
|
|
|
return response == "y" || response == "Y";
|
|
}
|
|
|
|
private:
|
|
|
|
time_t m_startTime;
|
|
};
|
|
\end{lstlisting}
|
|
|
|
To make the service use your timeout handler, you need to write a factory
|
|
class, to allow the service to create instances of the handler class. This
|
|
is required because the service can use several connections to the server
|
|
simultaneously, and each connection needs its own timeout handler.
|
|
|
|
\begin{lstlisting}
|
|
class myTimeoutHandlerFactory : public vmime::net::timeoutHandlerFactory {
|
|
|
|
public:
|
|
|
|
ref <timeoutHandler> create() {
|
|
|
|
return vmime::make_shared <myTimeoutHandler>();
|
|
}
|
|
};
|
|
\end{lstlisting}
|
|
|
|
Then, call the {\vcode setTimeoutHandlerFactory()} method on the service object
|
|
to set the timeout handler factory to use during the session:
|
|
|
|
\begin{lstlisting}
|
|
theService->setTimeoutHandlerFactory(vmime::make_shared <myTimeoutHandlerFactory>());
|
|
\end{lstlisting}
|
|
|
|
|
|
% ============================================================================
|
|
\newpage
|
|
\section{Secured connection using TLS/SSL}
|
|
|
|
\subsection{Introduction} % --------------------------------------------------
|
|
|
|
If you have enabled TLS support in VMime, you can configure messaging services
|
|
so that they use a secured connection.
|
|
|
|
Quoting from RFC-2246 - the TLS 1.0 protocol specification: \emph{`` The TLS
|
|
protocol provides communications privacy over the Internet. The protocol
|
|
allows client/server applications to communicate in a way that is designed
|
|
to prevent eavesdropping, tampering, or message forgery.''}
|
|
|
|
TLS has the following advantages:
|
|
|
|
\begin{itemize}
|
|
\item authentication: server identity can be verified;
|
|
\item privacy: transmission of data between client and server cannot be read
|
|
by someone in the middle of the connection;
|
|
\item integrity: original data which is transferred between a client and a
|
|
server can not be modified by an attacker without being detected.
|
|
\end{itemize}
|
|
|
|
\vnote{What is the difference between SSL and TLS? SSL is a protocol designed
|
|
by Netscape. TLS is a standard protocol, and is partly based on version 3 of
|
|
the SSL protocol. The two protocols are not interoperable, but TLS does
|
|
support a mechanism to back down to SSL 3.}
|
|
|
|
VMime offers two possibilities for using a secured connection:
|
|
|
|
\begin{itemize}
|
|
\item you can connect to a server listening on a special port (eg. IMAPS
|
|
instead of IMAP): this is the classical use of SSL, but is now deprecated;
|
|
\item connect to a server listening on the default port, and then begin a
|
|
secured connection: this is STARTTLS.
|
|
\end{itemize}
|
|
|
|
|
|
\subsection{Setting up a secured connection} % -------------------------------
|
|
|
|
\subsubsection{Connecting to a ``secured'' port} % ...........................
|
|
|
|
To use the classical SSL/TLS way, simply use the ``S'' version of the protocol
|
|
to connect to the server (eg. \emph{imaps} instead of \emph{imap}). This is
|
|
currently available for SMTP, POP3 and IMAP.
|
|
|
|
\begin{lstlisting}
|
|
vmime::shared_ptr <vmime::net::store> store =
|
|
theSession->getStore(vmime::utility::url("imaps://example.org"));
|
|
\end{lstlisting}
|
|
|
|
\subsubsection{Using STARTTLS} % .............................................
|
|
|
|
To make the service start a secured session using the STARTTLS method, simply
|
|
set the \emph{connection.tls} property:
|
|
|
|
\begin{lstlisting}
|
|
theService->setProperty("connection.tls", true);
|
|
\end{lstlisting}
|
|
|
|
\vnote{If, for some reason, a secured connection cannot be started, the
|
|
default behaviour is to fallback on a normal connection. To make
|
|
{\vcode connect()} fail if STARTTLS fails, set the
|
|
\emph{connection.tls.required} to \emph{true}.}
|
|
|
|
\subsection{Certificate verification} % --------------------------------------
|
|
|
|
\subsubsection{How it works} % ...............................................
|
|
|
|
If you tried the previous examples, a
|
|
{\vcode certificateException} might have been thrown.
|
|
This is because the default certificate verifier in VMime did not manage to
|
|
verify the certificate, and so could not trust it.
|
|
|
|
Basically, when you connect to a server using TLS, the server responds with
|
|
a list of certificates, called a certificate chain (usually, certificates are
|
|
of type X.509\footnote{And VMime currently supports only X.509 certificates}).
|
|
The certificate chain is ordered so that the first certificate is the subject
|
|
certificate, the second is the subject's issuer one, the third is the issuer's
|
|
issuer, and so on.
|
|
|
|
To decide whether the server can be trusted or not, you have to verify that
|
|
\emph{each} certificate is valid (ie. is trusted). For more information
|
|
about X.509 and certificate verification, see related articles on Wikipedia
|
|
\footnote{See \url{http://wikipedia.org/wiki/Public\_key\_certificate}}.
|
|
|
|
\subsubsection{Using the default certificate verifier} % .....................
|
|
|
|
The default certificate verifier maintains a list of root (CAs) and user
|
|
certificates that are trusted. By default, the list is empty. So, you have
|
|
to initialize it before using the verifier.
|
|
|
|
The algorithm\footnote{See
|
|
\url{http://wikipedia.org/wiki/Certification\_path\_validation\_algorithm}}
|
|
used is quite simple:
|
|
|
|
\begin{enumerate}
|
|
\item for every certificate in the chain, verify that the certificate has been
|
|
issued by the next certificate in the chain;
|
|
\item for every certificate in the chain, verify that the certificate is valid
|
|
at the current time;
|
|
\item ensure that the first certificate's subject name matches the hostname
|
|
of the server;
|
|
\item decide whether the subject's certificate can be trusted:
|
|
\begin{itemize}
|
|
\item first, verify that the the last certificate in the chain was
|
|
issued by a third-party that we trust (root CAs);
|
|
\item if the issuer certificate cannot be verified against root CAs,
|
|
compare the subject's certificate against the trusted certificates
|
|
(the certificates the user has decided to trust).
|
|
\end{itemize}
|
|
\end{enumerate}
|
|
|
|
First, we need some code to load existing X.509 certificates:
|
|
|
|
\begin{lstlisting}[caption={Reading a X.509 certificate from a file}]
|
|
vmime::shared_ptr <vmime::security::cert::X509Certificate>
|
|
loadX509CertificateFromFile(const std::string& path) {
|
|
|
|
std::ifstream certFile;
|
|
certFile.open(path.c_str(), std::ios::in | std::ios::binary);
|
|
|
|
if (!certFile) {
|
|
// ...handle error...
|
|
}
|
|
|
|
vmime::utility::inputStreamAdapter is(certFile);
|
|
vmime::shared_ptr <vmime::security::cert::X509Certificate> cert;
|
|
|
|
cert = vmime::security::cert::X509Certificate::import(is);
|
|
|
|
return cert;
|
|
}
|
|
\end{lstlisting}
|
|
|
|
Then, we can use the {\vcode loadX509CertificateFromFile} function to load
|
|
certificates and initialize the certificate verifier:
|
|
|
|
\begin{lstlisting}[caption={Using the default certificate verifier}]
|
|
vmime::shared_ptr <vmime::security::cert::defaultCertificateVerifier> vrf =
|
|
vmime::make_shared <vmime::security::cert::defaultCertificateVerifier>();
|
|
|
|
// Load root CAs (such as Verisign or Thawte)
|
|
std::vector <vmime::shared_ptr <vmime::security::cert::X509Certificate> > rootCAs;
|
|
|
|
rootCAs.push_back(loadX509CertificateFromFile("/path/to/root-ca1.cer");
|
|
rootCAs.push_back(loadX509CertificateFromFile("/path/to/root-ca2.cer");
|
|
rootCAs.push_back(loadX509CertificateFromFile("/path/to/root-ca3.cer");
|
|
|
|
vrf->setX509RootCAs(rootCAs);
|
|
|
|
// Then, load certificates that the user explicitely chose to trust
|
|
std::vector <vmime::shared_ptr <vmime::security::cert::X509Certificate> > trusted;
|
|
|
|
trusted.push_back(loadX509CertificateFromFile("/path/to/trusted-site1.cer");
|
|
trusted.push_back(loadX509CertificateFromFile("/path/to/trusted-site2.cer");
|
|
|
|
vrf->setX509TrustedCerts(trusted);
|
|
\end{lstlisting}
|
|
|
|
|
|
\subsubsection{Writing your own certificate verifier} % ......................
|
|
|
|
If you need to do more complex verifications on certificates, you will have to
|
|
write your own verifier. Your verifier should inherit from the
|
|
{\vcode vmime::security::cert::certificateVerifier} class and implement the
|
|
method {\vcode verify()}. Then, if the specified certificate chain is trusted,
|
|
simply return from the function, or else throw a
|
|
{\vcode certificateException}.
|
|
|
|
The following example shows how to implement an interactive certificate
|
|
verifier which relies on the user's decision, and nothing else (you SHOULD NOT
|
|
use this in a production application as this is obviously a serious security
|
|
issue):
|
|
|
|
\begin{lstlisting}[caption={A custom certificate verifier}]
|
|
class myCertVerifier : public vmime::security::cert::certificateVerifier {
|
|
|
|
public:
|
|
|
|
void verify(const vmime::shared_ptr <certificateChain>& certs) {
|
|
|
|
// Obtain the subject's certificate
|
|
vmime::shared_ptr <vmime::security::cert::certificate> cert = chain->getAt(0);
|
|
|
|
std::cout << std::endl;
|
|
std::cout << "Server sent a '" << cert->getType() << "'"
|
|
<< " certificate." << std::endl;
|
|
std::cout << "Do you want to accept this certificate? (Y/n) ";
|
|
std::cout.flush();
|
|
|
|
std::string answer;
|
|
std::getline(std::cin, answer);
|
|
|
|
if (answer.length() != 0 && (answer[0] == 'Y' || answer[0] == 'y')) {
|
|
return; // OK, we trust the certificate
|
|
}
|
|
|
|
// Don't trust this certificate
|
|
throw vmime::security::cert::certificateException();
|
|
}
|
|
};
|
|
\end{lstlisting}
|
|
|
|
\vnote{In production code, it may be a good idea to remember user's decisions
|
|
about which certificates to trust and which not. See {\vexample Example6} for
|
|
a basic cache implementation.}
|
|
|
|
Finally, to make the service use your own certificate verifier, simply write:
|
|
|
|
\begin{lstlisting}
|
|
theService->setCertificateVerifier(vmime::make_shared <myCertVerifier>());
|
|
\end{lstlisting}
|
|
|
|
\subsection{SSL/TLS Properties} % --------------------------------------------
|
|
|
|
If you want to customize behavior or set some options on TLS/SSL connection,
|
|
you may use the TLSProperties object, and pass it to the service session. The
|
|
TLS/SSL options must be set {\em before} creating any service with the session
|
|
(ie. before calling either {\vcode getStore()} or {\vcode getTransport()} on
|
|
the session), or they will not be used.
|
|
|
|
The following example shows how to set the cipher suite preferences for TLS:
|
|
|
|
\begin{lstlisting}[caption={Setting TLS cipher suite preferences}]
|
|
vmime::shared_ptr <vmime::net::session> sess = /* ... */;
|
|
|
|
vmime::shared_ptr <vmime::net::tls::TLSProperties> tlsProps =
|
|
vmime::make_shared <vmime::net::tls::TLSProperties>();
|
|
|
|
// for OpenSSL
|
|
tlsProps->setCipherString("HIGH:!ADH:@STRENGTH");
|
|
|
|
// for GNU TLS
|
|
tlsProps->setCipherString("NORMAL:%SSL3_RECORD_VERSION");
|
|
|
|
sess->setTLSProperties(tlsProps);
|
|
\end{lstlisting}
|
|
|
|
Please note that the cipher suite string format and meaning depend on the
|
|
underlying TLS library (either OpenSSL or GNU TLS):
|
|
|
|
\begin{itemize}
|
|
\item for GNU TLS, read this: \newline
|
|
\url{http://gnutls.org/manual/html\_node/Priority-Strings.html}
|
|
|
|
\item for OpenSSL, read this: \newline
|
|
\url{http://www.openssl.org/docs/apps/ciphers.html#CIPHER\_STRINGS}
|
|
\end{itemize}
|
|
|
|
You may also set cipher suite preferences using predefined constants that
|
|
map to generic security modes:
|
|
|
|
\begin{lstlisting}[caption={Setting TLS cipher suite preferences using predefined modes}]
|
|
sess->setCipherSuite(vmime::net::tls::TLSProperties::CIPHERSUITE_HIGH);
|
|
\end{lstlisting}
|
|
|
|
The following constants are available:
|
|
|
|
\noindent\begin{tabularx}{1.0\textwidth}{|l|X|}
|
|
\hline
|
|
{\bf Constant} &
|
|
{\bf Meaning} \\
|
|
\hline
|
|
CIPHERSUITE\_HIGH &
|
|
High encryption cipher suites ($>$ 128 bits) \\
|
|
\hline
|
|
CIPHERSUITE\_MEDIUM &
|
|
Medium encryption cipher suites ($>=$ 128 bits) \\
|
|
\hline
|
|
CIPHERSUITE\_LOW &
|
|
Low encryption cipher suites ($>=$ 64 bits) \\
|
|
\hline
|
|
CIPHERSUITE\_DEFAULT &
|
|
Default cipher suite (actual cipher suites used depends
|
|
on the underlying SSL/TLS library) \\
|
|
\hline
|
|
\end{tabularx}
|
|
|
|
|
|
% ============================================================================
|
|
\section{Tracing connection}
|
|
|
|
Connection tracing is used to log what is sent and received on the wire
|
|
between the client and the server, and may help debugging.
|
|
|
|
First, you have to create your own tracer, which must implement the
|
|
{\vcode vmime::net::tracer} interface. Here is an example of a tracer which
|
|
simply logs to the standard output:
|
|
|
|
\begin{lstlisting}[caption={A simple tracer}]
|
|
class myTracer : public vmime::net::tracer {
|
|
|
|
public:
|
|
|
|
myTracer(const vmime::string& proto, const int connectionId)
|
|
: m_proto(proto),
|
|
m_connectionId(connectionId) {
|
|
|
|
}
|
|
|
|
// Called by VMime to trace what is sent on the socket
|
|
void traceSend(const vmime::string& line) {
|
|
|
|
std::cout << "[" << m_proto << ":" << m_connectionId
|
|
<< "] C: " << line << std::endl;
|
|
}
|
|
|
|
// Called by VMime to trace what is received from the socket
|
|
void traceReceive(const vmime::string& line) {
|
|
|
|
std::cout << "[" < < m_proto << ":" << m_connectionId
|
|
<< "] S: " << line << std::endl;
|
|
}
|
|
|
|
private:
|
|
|
|
const vmime::string m_proto;
|
|
const int m_connectionId;
|
|
};
|
|
\end{lstlisting}
|
|
|
|
Also create a factory class, used to instanciate your tracer objects:
|
|
|
|
\begin{lstlisting}
|
|
class myTracerFactory : public vmime::net::tracerFactory {
|
|
|
|
public:
|
|
|
|
vmime::shared_ptr <vmime::net::tracer> create(
|
|
const vmime::shared_ptr <vmime::net::service>& serv,
|
|
const int connectionId
|
|
) {
|
|
|
|
return vmime::make_shared <myTracer>(
|
|
serv->getProtocolName(), connectionId
|
|
);
|
|
}
|
|
};
|
|
\end{lstlisting}
|
|
|
|
Next, we have to tell VMime to use it. When you create your service
|
|
(either store or transport), simply call the {\vcode setTracerFactory}
|
|
on the service and pass an instance of your factory class:
|
|
|
|
\begin{lstlisting}[caption={Enabling tracer on a connection}]
|
|
vmime::shared_ptr <vmime::net::transport> store =
|
|
session->getStore("imaps://user:password@imap.myserver.com");
|
|
|
|
// Enable tracing communication between client and server
|
|
store->setTracerFactory(vmime::make_shared <myTracerFactory>());
|
|
\end{lstlisting}
|
|
|
|
That's all! Now, everything which is sent on/received from the socket
|
|
will be logged using your tracer object. Here is an example of a trace
|
|
session for IMAP:
|
|
|
|
\begin{verbatim}
|
|
[imaps:1] S: * OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR
|
|
LOGIN-REFERRALS ID ENABLE IDLE AUTH=PLAIN] Dovecot ready.
|
|
[imaps:1] C: a001 AUTHENTICATE PLAIN
|
|
[imaps:1] S: +
|
|
[imaps:1] C: {...SASL exchange: 52 bytes of data...}
|
|
[imaps:1] S: a001 OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR
|
|
LOGIN-REFERRALS ID ENABLE IDLE SORT SPECIAL-USE QUOTA] Logged in
|
|
[imaps:1] C: a002 LIST "" ""
|
|
[imaps:1] S: * LIST (\Noselect) "." ""
|
|
[imaps:1] S: a002 OK List completed.
|
|
[imaps:1] C: a003 CAPABILITY
|
|
[imaps:1] S: * CAPABILITY IMAP4rev1 LITERAL+ SASL-IR
|
|
LOGIN-REFERRALS ID ENABLE IDLE SORT SPECIAL-USE QUOTA
|
|
[imaps:1] S: a003 OK Capability completed.
|
|
[imaps:1] C: a003 SELECT INBOX (CONDSTORE)
|
|
[imaps:1] S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft
|
|
$NotJunk NonJunk JunkRecorded $MDNSent NotJunk $Forwarded
|
|
Junk $Junk Forwarded $MailFlagBit1)
|
|
[imaps:1] S: * OK [PERMANENTFLAGS (\Answered \Flagged \Deleted
|
|
\Seen \Draft $NotJunk NonJunk JunkRecorded $MDNSent NotJunk
|
|
$Forwarded Junk $Junk Forwarded $MailFlagBit1 \*)]
|
|
Flags permitted.
|
|
[imaps:1] S: * 104 EXISTS
|
|
[imaps:1] S: * 0 RECENT
|
|
[imaps:1] S: * OK [UNSEEN 6] First unseen.
|
|
[imaps:1] S: * OK [UIDVALIDITY 1268127585] UIDs valid
|
|
[imaps:1] S: * OK [UIDNEXT 32716] Predicted next UID
|
|
[imaps:1] S: * OK [HIGHESTMODSEQ 148020] Highest
|
|
[imaps:1] S: a003 OK [READ-WRITE] Select completed.
|
|
\end{verbatim}
|
|
|
|
Please note that no sensitive data (ie. login or password) will be traced.
|
|
Same, {\em blob} data such as message content or SASL exchanges will be logged
|
|
as a marker which indicates how many bytes were sent/received (eg. "{...SASL
|
|
exchange: 52 bytes of data...}"").
|