This section will let you learn VMime by example.
The most part of what you can do with VMime is explained in this page.
component class
In VMime, all the components of a message inherit from the same class component.
This includes the message itself (classes message and bodyPart),
the header, the header fields and the value of each header field, the body and all the
parts in the message.
The class component provide a common interface for parsing or generating all
these components (methods parse() and generate()). It also provides
additional functions to get some information about the parsing process or the structure
(methods getParsedOffset(), getParsedLength() and
getChildComponents()).
Vmime also provides a set of classes corresponding to the basic types found in a message,
for example a mailbox, a mailbox list, date/time information, media type, etc. They all
inherit from component too.
text type
The text type can contain text in multiple charsets. It is composed
of one or more words, each with a different charset. For example, the content
of the field 'Subject:' is of type text.
You can easily make a text object from a simple string
using the static function text::newFromString():
text* text::newFromString(const string& in, const charset& ch, text* generateInExisting = NULL);
The following code illustrates the use of this function:
vmime::string inText = "Linux dans un téléphone mobile"; vmime::charset inCharset = "iso-8859-1"; vmime::text outText; outText.createFromString(inText, inCharset); // 'outText' now contains 3 words: // . <us-ascii> "Linux dans un " // . <iso-8859-1> "téléphone " // . <us-ascii> "mobile"
or :
vmime::text* myText = vmime::newFromString(inText, inCharset); // ... delete (myText);
VMime code may throw exceptions in many different situations: an unexpected error occured, an operation is not supported, etc. You should catch them if you want to report failures to the user. This is also useful for debugging your program.
VMime exceptions support chaining: an exception can be encapsulated into another exception
to hide implementation details. The exception::other() function returns the
next exception in the chain, or NULL.
Following is an example code for catching and showing VMime exceptions to the user:
std::ostream& operator<<(std::ostream& os, const vmime::exception& e)
{
os << "* vmime::exceptions::" << e.name() << std::endl;
os << " what = " << e.what() << std::endl;
// More information for special exceptions
if (dynamic_cast <const vmime::exceptions::command_error*>(&e))
{
const vmime::exceptions::command_error& cee =
dynamic_cast <const vmime::exceptions::command_error&>(e);
os << " command = " << cee.command() << std::endl;
os << " response = " << cee.response() << std::endl;
}
if (dynamic_cast <const vmime::exceptions::connection_greeting_error*>(&e))
{
const vmime::exceptions::connection_greeting_error& cgee =
dynamic_cast <const vmime::exceptions::connection_greeting_error&>(e);
os << " response = " << cgee.response() << std::endl;
}
if (dynamic_cast <const vmime::exceptions::authentication_error*>(&e))
{
const vmime::exceptions::authentication_error& aee =
dynamic_cast <const vmime::exceptions::authentication_error&>(e);
os << " response = " << aee.response() << std::endl;
}
if (dynamic_cast <const vmime::exceptions::filesystem_exception*>(&e))
{
const vmime::exceptions::filesystem_exception& fse =
dynamic_cast <const vmime::exceptions::filesystem_exception&>(e);
os << " path = " << vmime::platformDependant::getHandler()->
getFileSystemFactory()->pathToString(fse.path()) << std::endl;
}
if (e.other() != NULL)
os << *e.other();
return os;
}
...
try
{
// ...some call to VMime...
}
catch (vmime::exception& e)
{
std::cerr << e; // VMime exception
}
catch (std::exception& e)
{
std::cerr << e.what(); // standard exception
}
contentHandler object is an abstraction for data source. It is used everywhere VMime needs some data when building a message: body part contents, attachment data, etc.
There are 3 types of content handlers:
Here are some examples of use:
vmime::stringContentHandler cts("Body contents");
my_body_part.getBody()->setContents(cts);
std::ifstream* file = new std::ifstream();
file->open(filename.c_str(), std::ios::in | std::ios::binary);
if (!*file)
{
// ...handle error...
}
vmime::inputStream* is = new vmime::utility::inputStreamPointerAdapter(file, true);
vmime::streamContentHandler cts(is, /* no length specified */ 0, /* we own it */ true);
my_body_part.getBody()->setContents(cts);
The following example use the messageBuilder component to construct
a very simple message, with only text (RFC-[2]822 format). Don't forget to
include necessary VMime initializations at
the beginning of your program!
try
{
vmime::messageBuilder mb;
// Fill in some header fields and message body
mb.setExpeditor(vmime::mailbox("sender@server.com"));
mb.getRecipients().appendAddress(new vmime::mailbox("recipient@server.com"));
mb.getBlindCopyRecipients().appendAddress(new vmime::mailbox("recipient-bcc@server.com"));
mb.setSubject(vmime::text("My first message generated with VMime"));
mb.getTextPart()->setCharset(vmime::charsets::ISO8859_15);
mb.getTextPart()->setText
(vmime::stringContentHandler("I'm writing this short text to test the" \
"construction of a message using vmime::messageBuilder component."));
// Message construction
vmime::message* msg = mb.construct();
// Generate text corresponding to the message
vmime::string dataToSend = msg->generate();
// ...send the message...
// Destruct the message object
delete (msg);
}
// VMime exception
catch (vmime::exception& e)
{
std::cerr << e;
}
// Standard exception
catch (std::exception& e)
{
std::cerr << "std::exception: " << e.what() << std::endl;
}
Let's take the code of the previous example and add some lines of code to include an attachment in your message. The attachment data will be read from a file:
try
{
vmime::messageBuilder mb;
// ... code of previous example (before .construct()!)
// Add an attachment
vmime::fileAttachment* a = new vmime::fileAttachment
(
"/full/path/to/file", // full path to file
vmime::mediaType("application/octet-stream"), // content type
vmime::text("My first attachment")) // description
);
// You can set some infos on the file attachment
a->getFileInfo().setFilename("file_name");
a->getFileInfo().setCreationDate(vmime::datetime("30 Apr 2003 14:30:00 +0200"));
mb.appendAttachment(a); // add this attachment to the message
// ...
}
// VMime exception
catch (vmime::exception& e)
{
std::cerr << e;
}
// Standard exception
catch (std::exception& e)
{
std::cerr << "std::exception: " << e.what() << std::endl;
}
The messageBuilder is a very powerful component. As a proof, see the
following code that constructs a message in which the text is available in two
formats (HTML and plain), and with an embedded object (an image in the HTML text).
try
{
vmime::messageBuilder mb;
// Fill in some header fields
mb.setExpeditor(vmime::mailbox("sender@server.com"));
mb.getRecipients().appendAddress(new vmime::mailbox("recipient@server.com"));
mb.getBlindCopyRecipients().appendAddress(new vmime::mailbox("recipient-bcc@server.com"));
mb.setSubject(vmime::text("My first message generated with VMime"));
// Set the content-type to "text/html": a text part factory must be available
// for the type you are using. The following code will make the message builder
// construct the two text parts.
mb.constructTextPart(vmime::mediaType
(vmime::mediaTypes::TEXT, vmime::mediaTypes::TEXT_HTML));
// Set the contents of the text parts; the message is available in two format:
// HTML and plain text. The HTML format also includes an embedded image.
vmime::htmlTextPart& textPart = dynamic_cast<vmime::htmlTextPart&>(*mb.getTextPart());
// -- add the JPEG image (the returned identifier is used to identify the embedded
// -- object in the HTML text, the famous "CID", or "Content-Id")
vmime::string id = textPart.addObject("<...TODO: image data...>",
vmime::mediaType(vmime::mediaTypes::IMAGE, vmime::mediaTypes::IMAGE_JPEG));
// -- set the text
textPart.setCharset(vmime::charsets::ISO8859_15);
textPart.setText(vmime::stringContentHandler(vmime::string
("This is the <b>HTML text</b>, and the image:<br/>" \
"<img src=\"") + id + vmime::string("\"/>")));
textPart.setPlainText(vmime::stringContentHandler("This is the plain text."));
// Message construction
vmime::message* msg = mb.construct();
// Generate text corresponding to the message
vmime::string dataToSend = msg->generate();
// ...send the message...
// Destruct the message object
delete (msg);
}
// VMime exception
catch (vmime::exception& e)
{
std::cerr << e;
}
// Standard exception
catch (std::exception& e)
{
std::cerr << "std::exception: " << e.what() << std::endl;
}
The messageParser is a powerful and simple solution to extract
common data from messages: text parts and attachments. Let's see some simple
uses...
The following example illustrates the use of the messageParser component
to enumerate the text parts in a message.
try
{
vmime::messageParser mp("<...message data...>");
// Enumerate text parts in the message
for (int i = 0 ; i < mp.getTextPartCount() ; ++i)
{
const vmime::textPart& part = *mp.getTextPartAt(i);
// Output part content-type
std::cout << part.type().generate() << std::endl;
// text/html
if (part.type().subType() == vmime::mediaTypes::TEXT_HTML)
{
const vmime::htmlTextPart& hp = dynamic_cast<const vmime::htmlTextPart&>(part);
// HTML text is in hp.getText()
// Plain text is in hp.getPlainText()
// Enumerate embedded objects (images, etc.)
for (int j = 0 ; j < hp.getObjectCount() ; ++j)
{
const vmime::htmlTextPart::embeddedObject& obj = *hp.getObjectAt(j);
// Identifier (content-id or content-location) in obj.getId()
// Data in obj.getData()
}
}
// text/plain
else
{
const vmime::textPart& tp = dynamic_cast<const vmime::textPart&>(part);
// Text in tp.getText()
}
}
}
// VMime exception
catch (vmime::exception& e)
{
std::cerr << e;
}
// Standard exception
catch (std::exception& e)
{
std::cerr << "std::exception: " << e.what() << std::endl;
}
With the messageParser component, you can also enumerate and extract
attachments from a message. The code is very simple:
try
{
vmime::messageParser mp("<...message data...>");
// Enumerate attachments in the message
for (int i = 0 ; i < mp.getAttachmentCount() ; ++i)
{
const vmime::attachment& attach = *mp.getAttachmentAt(i);
// Media type in attach.getType()
// Description in attach.getDescription()
// Attachment data in attach.getData()
}
}
// VMime exception
catch (vmime::exception& e)
{
std::cerr << e;
}
// Standard exception
catch (std::exception& e)
{
std::cerr << "std::exception: " << e.what() << std::endl;
}
Before starting, you should take a look at the example6 sample program,
which demonstrates the use of the messaging module.
When connecting to services, you may need to supply a username and a password
to the server. In VMime, the authenticator object is responsible
for this task.
If you don't provide an authenticator, and if credentials are needed to connect to the server, they will be read from the session properties (see below for knowing about creating a session).
The following code shows an example of how to implement a custom authenticator, by reading the credentials from the standard input:
class my_auth : public vmime::messaging::authenticator
{
const vmime::messaging::authenticationInfos requestAuthInfos() const
{
vmime::string username, password;
std::cout << "Username: "; std::cout.flush();
std::cin >> username;
std::cout << "Password: "; std::cout.flush();
std::cin >> password;
return (vmime::messaging::authenticationInfos(username, password));
}
};
Creating a session is as easy as instanciating an object (the messaging::session
object). But you will also have to initialize this object by setting some
properties on it:
vmime::messaging::session sess; sess.getProperties()["store.protocol"] = "imap"; sess.getProperties()["transport.protocol"] = "smtp";
The store.protocol and transport.protocol properties
indicate which protocols to use for message store and message transport services,
respectively.
Some properties are global to this session, others are specific to one service or to one protocol. For example, you can set properties to enable options for a protocol (eg. enable or disable APOP authentication for POP3).
The following code snippet use the serviceFactory object to enumerate
the available services (stores and transports), and print their name and their
settable properties on the standard output. This is an excerpt from example7.
You can also retrieve the type and the default value of each property. This permits setting service properties in a generic manner.
// Get a reference to the serviceFactory object
vmime::messaging::serviceFactory* sf = vmime::messaging::serviceFactory::getInstance();
std::cout << "Available messaging services:" << std::endl;
for (int i = 0 ; i < sf->getServiceCount() ; ++i)
{
const vmime::messaging::serviceFactory::registeredService& srv = *sf->getServiceAt(i);
// Print the service name (protocol) and the default port
std::cout << " * " << srv.name()
<< " (" << srv.getInfos().getDefaultPort() << ")" << std::endl;
// Enumerate possible properties for this service
std::vector <vmime::string> props = srv.getInfos().getAvailableProperties();
for (std::vector <vmime::messaging::serviceInfos::property>::const_iterator it = props.begin() ;
it != props.end() ; ++it)
{
const vmime::messaging::serviceInfos::property& p = *it;
const vmime::string name = serv.getInfos().getPropertyPrefix() + p.getName();
vmime::string type;
switch (p.getType())
{
case vmime::messaging::serviceInfos::property::TYPE_INTEGER: type = "TYPE_INTEGER"; break;
case vmime::messaging::serviceInfos::property::TYPE_STRING: type = "TYPE_STRING"; break;
case vmime::messaging::serviceInfos::property::TYPE_BOOL: type = "TYPE_BOOL"; break;
default: type = "(unknown)"; break;
}
vmime::string flags;
if (p.getFlags() & vmime::messaging::serviceInfos::property::FLAG_REQUIRED)
flags += " FLAG_REQUIRED";
if (p.getFlags() & vmime::messaging::serviceInfos::property::FLAG_HIDDEN)
flags += " FLAG_HIDDEN";
std::cout << " - " << serv.getInfos().getPropertyPrefix() + p.getName();
std::cout << " (type=" << type << ", flags=" << flags;
std::cout << ", defaultValue=" << p.getDefaultValue() << ")" << std::endl;
}
}
Since version 0.6.1, it is possible to get a service instance given an URL. The URL should
at least specify the protocol to use and the server address. It is also possible to specify
the username and password for the account (if not specified, an authenticator
object is used).
Here are some examples:
// Maildir
vmime::messaging::url url("maildir://localhost/home/vincent/Mail"); // Host not used
vmime::messaging::store* store = session.getStore(url);
// IMAP
vmime::messaging::url url("imap://vincent:XXXXX@myserver.org:143/");
vmime::messaging::store* store = session.getStore(url);
// POP3
vmime::messaging::url url("pop3://vincent:XXXXX@myserver.org/");
vmime::messaging::store* store = session.getStore(url);
// SMTP
vmime::messaging::url url("smtp://myserver.org/");
vmime::messaging::transport* transport = session.getTransport(url);
// Sendmail
vmime::messaing::url url("sendmail://localhost/"); // Host not used (always localhost)
vmime::messaging::transport* transport = session.getTransport(url);
The following example illustates the use of the transport service to send a message over the Internet using the SMTP protocol:
try
{
// Session initialization (global properties)
vmime::messaging::session sess;
sess.getProperties()["transport.protocol"] = "smtp";
// Transport protocol configuration (service-specific properties)
vmime::messaging::transport* tr = sess.getTransport();
// -- set the server address and port (if not specified, default is used)
sess.getProperties()[tr->getInfos().getPropertyPrefix() + "server.address"] = "smtp.mydomain.com";
//sess.getProperties()[tr->getInfos().getPropertyPrefix() + "server.port"] = 21;
// -- you can also set the authentication credentials
//sess.getProperties()[tr->getInfos().getPropertyPrefix() + "auth.username"] = "username";
//sess.getProperties()[tr->getInfos().getPropertyPrefix() + "auth.password"] = "password";
//sess.getProperties()[tr->getInfos().getPropertyPrefix() + "options.need-authentication"] = true;
// NOTE: another way for providing authentication information is to
// create a authenticator object and creating you service as following:
//
// class my_auth : public vmime::messaging::authenticator { ... };
//
// my_auth auth = new my_auth;
// vmime::messaging::transport* tr = sess.getTransport(auth);
//
//
// Connect to the server
tr->connect();
// Expeditor
vmime::mailbox from("me@somewhere.com");
// Recipients list
vmime::mailboxList to;
to.appendMailbox(new vmime::mailbox("you@somewhere.com"));
to.appendMailbox(new vmime::mailbox("somebody.else@anywhere.com"));
vmime::string contents("[...message data: header body...]");
vmime::utility::inputStreamAdapter is(contents);
tr->send(from, to, is, contents.length());
// Note: you could also write this:
// vmime::message msg;
// ...
// tr->send(&msg);
// Disconnect from server
tr->disconnect();
delete (tr);
//delete (auth);
}
catch (vmime::exception& e)
{
std::cerr << e;
}
catch (std::exception& e)
{
std::cerr << "std::exception: " << e.what() << std::endl;
}
The following example illustates the use of the store service to connect to an IMAP server, and retrieve the first message of the INBOX.
try
{
// If no authenticator is given in argument to getStore(), a default one
// is used. Its behaviour is to get the user credentials from the
// session properties "auth.username" and "auth.password".
vmime::messaging::store* st = sess.getStore();
// Store protocol configuration
sess.getProperties()[st->getInfos().getPropertyPrefix() + "auth.username"] = "username";
sess.getProperties()[st->getInfos().getPropertyPrefix() + "auth.password"] = "password";
sess.getProperties()[st->getInfos().getPropertyPrefix() + "server.address"] = "imap.mydomain.com";
//sess.getProperties()[st->getInfos().getPropertyPrefix() + "server.port"] = 143;
//sess.getProperties()[st->getInfos().getPropertyPrefix() + "server.socket-factory"] = "default";
// Some options examples if your are using POP3: try to use APOP authentication,
// and fallback to basic authentication if APOP is not available.
//sess.getProperties()[st->getInfos().getPropertyPrefix() + "options.apop"] = true;
//sess.getProperties()[st->getInfos().getPropertyPrefix() + "options.apop.fallback"] = true;
// Connect to the server
st->connect();
// Open the default folder in this store
vmime::messaging::folder* f = st->getDefaultFolder();
f->open(vmime::messaging::folder::MODE_READ_ONLY);
std::cout << f->getMessageCount() << " message(s) in your inbox" << std::endl;
// Get a pointer to the first message
vmime::messaging::message* m = f->getMessage(1);
// To fetch the header
f->fetchMessage(m, vmime::messaging::folder::FETCH_ENVELOPE |
vmime::messaging::folder::FETCH_CONTENT_INFO);
std::ostringstream header;
header << m->getHeader().generate();
// To retrieve the whole message
std::ostringstream content;
vmime::utility::outputStreamAdapter os(content);
m->extract(os);
// Delete the message object (this does NOT remove it on the server)
delete (m);
// Close the folder
f->close(true);
delete (f);
// Disconnect from the server
st->disconnect();
delete (st);
}
// ...
catch (vmime::exception& e)
{
std::cerr << e;
}
catch (std::exception& e)
{
std::cerr << "std::exception: " << e.what() << std::endl;
}
The following code prints the MIME structure of a message on the standard output:
void printStructure(const vmime::messaging::structure& s, int level = 0)
{
// Parts are numbered from 1 to total number of parts
for (int i = 1 ; i <= s.getCount() ; ++i)
{
const vmime::messaging::part& part = s[i];
for (int j = 0 ; j < level * 2 ; ++j)
std::cout << " "; // indentation
// Print information about this part
std::cout << part.getNumber() << ". "
<< part.getType().generate()
<< " [" << part.getSize() << " byte(s)]"
<< std::endl;
// Recursively prints the structure
printStructure(part.getStructure(), level + 1);
}
}
...
// Fetch message structure
myFolder->fetchMessage(myMessage, vmime::messaging::folder::FETCH_STRUCTURE);
// Print MIME structure on the standard output
printStructure(myMessage->getStructure());
Fetching MIME structure is not supported by all protocols (actually, only IMAP).
If you try to fetch structure with a protocol that do not support this, a
operation_not_supported exception will be thrown. You will have
to extract the full message and use VMime parser tools (vmime::message::parse())
to get the structure.
Let's see how to extract the contents of a specific part in a message (this may
not be supported by the protocol, in which case a operation_not_supported
exception is thrown). Suppose that the message has the following structure:
This code extracts the contents of the "text/html" part (1.2.1) and streams it to the standard output:
myMessage->extractPart(myMessage->getStructure()[1][2][1], std::cout, NULL);
Note that you can also extract some portion of a specific part (this is called
partial fetch, and may not be supported by the protocol: a partial_fetch_not_supported
exception will be thrown). For more information, see the prototypes of the functions
extract and extractPart in the
class message.
There are a lot of things that can be done with messaging stores: retrieve/set
message flags, create/delete/rename folders, extract a specific MIME part (IMAP
protocol supports this, for example)... You can learn a lot by reading the
documentation for messaging::store, messaging::folder
and messaging::message abstract classes in the
Doxygen-generated documentation.
By default, when connecting to messaging servers, VMime waits indefinitely for the server response after each request. You can override this behaviour by setting up a time-out handler.
The abstract class timeoutHandler defines an interface for managing
a time-out counter and handling the case where response time is elapsed:
const bool isTimeOut(); void resetTimeOut(); const bool handleTimeOut();
The resetTimeOut() function should reset an internal counter for
time-out. The isTimeOut() function should return true
if, at the time of the call, the time is elapsed. Then, the handleTimeOut()
function will be called to handle the time-out (return true to
continue waiting for the response (and reseting the counter), or false
to cancel the current operation).
The following code show an example implementation for a simple time-out handler:
class my_timeout_handler : public vmime::messaging::timeoutHandler
{
public:
// By default, time is out after 60 seconds
my_timeout_handler(const unsigned int delay = 60)
: m_delay(delay)
{
}
const bool isTimeOut()
{
const unsigned int current = platformDependant::getHandler()->getUnixTime();
return (current >= m_start + m_delay);
}
void resetTimeOut()
{
// Reset the time-out counter
m_start = platformDependant::getHandler()->getUnixTime();
}
const bool handleTimeOut()
{
std::cout << "The operation timed-out while waiting for a server response.\n";
std::cout << "Cancel the operation?" << std::cout;
char answer;
std::cin >> answer;
// Return 'true' to continue (the time-out will be reset automatically),
// or 'false' to cancel the current operation
return (answer == 'n');
}
private:
const unsigned int m_delay; // time-out delay
unsigned int m_start; // start time
};
Now, let's see how to make a service use your time-out handler. Time-out handler objects
are instanciated using a factory: the timeoutHandlerFactory. You can specify
which time-out handler to use by setting the timeout.handler property on
this service:
vmime::messaging::session sess; sess.getProperties()["store.protocol"] = "pop3"; vmime::messaging::store* st = sess.getStore(); // Store protocol configuration sess.getProperties()[st->getInfos().getPropertyPrefix() + "auth.username"] = "username"; sess.getProperties()[st->getInfos().getPropertyPrefix() + "auth.password"] = "password"; sess.getProperties()[st->getInfos().getPropertyPrefix() + "server.address"] = "mail.myserver.com"; sess.getProperties()[st->getInfos().getPropertyPrefix() + "timeout.handler"] = "my_pop3handler"; ...
Finally, you have to implement the factory for your time-out handler, and to write the
code for the getTimeoutHandlerFactory() function in your platform-dependant
handler:
class my_timeout_handler_factory
{
my_timeout_handler* create()
{
return new my_timeout_handler(120);
}
};
class my_handler : public vmime::platformDependant::handler
{
private:
my_timeout_handler_factory* m_thf;
public:
my_handler()
: m_thf(new my_timeout_handler_factory)
{
}
~my_handler()
{
delete (m_thf);
}
// ...
vmime::messaging::timeoutHandlerFactory* getTimeoutHandlerFactory(const string& name) const
{
// Here, you could use the "name" parameter to return a different
// factory depending on, for example, the protocol type. This is
// the text you set in the "time.handler" property.
//if (name == "my_pop3handler")
// return ...
return m_thf;
}
};
When you are downloading a message from a store or sending data to a transport service, your
application can be notified of how much of the task has been done by using the
progressionListener object. The following operations can make use of the
progression listener:
messaging::folder::fetchMessages().messaging::message::extract() and
messaging::message::extractPart().messaging::transport::send().All you have to do to tell these functions to use your progression listener is to give a pointer to it in argument (usually the last argument; for more information, see the class documentation).
A progression listener object must inherit from messaging::progressionListener and
implement the following functions:
const bool cancel() const; void start(const int predictedTotal); void progress(const int current, const int currentTotal); void stop(const int total);
The cancel() function should return true to cancel the operation.
Be conscious that this is implementation-dependant: the messaging protocol may not support
this, and in that case, the result is ignored.
start() is called when the operation has started (initialization). The parameter
predictedTotal indicate the number of units the operation may last (this can
be a number of bytes, a percentage...). This may change during the operation.
The progress() function is called each time an amount of progress units has
been completed. The current parameter indicates the total number of units
completed, and currentTotal the new total (this may be the same as in
start()).
Finally, the stop() function is called when the operation is finished. The
total parameter indicates how many units the operation lasted.
The following code is an example for implementing a progression listener:
class my_progress_listener : public vmime::messaging::progressionListener
{
protected:
virtual ~progressionListener() { }
public:
// Return true to cancel the operation (this is implementation-dependant:
// messaging protocol may not support this)
const bool cancel()
{
return false;
}
// Progression notification
void start(const int predictedTotal)
{
std::cout << "Operation started..." << std::endl;
std::cout << "0%";
std::cout.flush();
}
void progress(const int current, const int currentTotal)
{
const int percent = (100 / currentTotal) * current;
std::cout << "\r" << percent << "%";
std::cout.flush();
}
void stop(const int total)
{
std::cout << "\n";
std::cout << "Operation finished!" << std::endl;
}
};
And the code to use it:
... vmime::messaging::message* msg = folder->getMessage(1); my_progress_listener progress; std::ostringstream content; vmime::utility::outputStreamAdapter os(content); msg->extract(os, &progress); ...
vmime::text
vmime::text can contain text in multiple charsets (see The text
type). You may want to extract the whole text into one unique charset (for example, with an Unicode
encoding not to loose any information). This is the goal of the getConvertedText() method.
For example, the following code:
vmime::text txt;
txt.appendWord(new vmime::word("D\x82codage de texte", vmime::charset(vmime::charsets::ISO_8859_1)));
txt.appendWord(new vmime::word(" avec VMime", vmime::charset(vmime::charsets::US_ASCII)));
const vmime::string dec = txt.getConvertedText(vmime::charsets::UTF_8);
std::cout << dec << std::endl;
will output the string "Décodage de texte avec VMime" in the UTF-8 encoding.
To encode or decode data, you have to get a new instance to an encoder
object. Encoder instances are created by encoderFactory:
vmime::encoder* enc = vmime::encoderFactory::getInstance()->create("base64");
Then, you can call either the encode() or decode() method on
the object. Please note that encoding/decoding is done using streams.
const utility::stream::size_type encode(utility::inputStream& in, utility::outputStream& out); const utility::stream::size_type decode(utility::inputStream& in, utility::outputStream& out);
You can encode or decode a string using a stream adapter:
vmime::string inString = "Data to decode"; vmime::utility::inputStreamStringAdapter in(inString); vmime::string outString; vmime::utility::outputStreamStringAdapter out(outString); enc->decode(in, out); std::cout << "Decoded data = " << outString << std::endl;
By default, unknown (non-standard) header fields are of type defaultField (the
contents is then interpreted as simple text). You can change this behaviour and tell VMime
what type of field (class) to use for a particular name. This is called custom
header fields.
For example, to tell VMime the field with name "X-MyField:" should contain a date (the same
format as the field 'Date:'), and that we want to access it like a dateField
object, you can write:
// Register the name at the header field factory
vmime::headerFieldFactory::getInstance()->registerName <vmime::dateField>("X-MyField");
// Now, the fields with name "X-MyField:" should be accessed as objects of
// type "dateField" (and no more "defaultField")...
dynamic_cast <vmime::dateField&>(*myMessage->getHeader()->getField("X-MyField"))
= vmime::datetime("21 May 2003 00:03:00 +0200");
NOTE: standard fields (eg. Date, From, To, ...) can not be reinterpreted (re-registering them
at the header field factory has no effect). To know the complete list of the fields, please
see the related constants vmime::fields::XXX in file constants.hpp.
By default, the following encodings are available: base64, quoted-printable, uuencode, 7bit, 8bit, binary. You can define your own encodings and tell VMime to use them when needed.
To do this, you have to create a new class that implements the vmime::encoder
interface, and register it at the encoder factory (object encoderFactory).
The following example illustrates the use of custom encodings:
class myEncoder : public vmime::encoder
{
const utility::stream::size_type encode
(utility::inputStream& in, utility::outputStream& out)
{
in.reset(); // may not work...
// No encoding performed (TODO: write your own encoding algorithm)
return (utility::bufferedStreamCopy(in, out));
}
const utility::stream::size_type decode
(utility::inputStream& in, utility::outputStream& out)
{
in.reset(); // may not work...
// No decoding performed (TODO: write your own decoding algorithm)
return (utility::bufferedStreamCopy(in, out));
}
};
...
// Register the new encoding
vmime::encoderFactory::getInstance()->registerName <myEncoder>("x-my-encoding");
// From now, VMime will use your encoder "myEncoder" when it will need to encode
// or decode some contents with "Content-Transfer-Encoding:" set to "x-my-encoding".