XOAUTH2 authentication (GMail)

It’s been a while! Today, XOAUTH2 authentication mechanism was implemented into VMime, thanks to Kevin Xi. This SASL authentication mechanism is used by Google GMail.

Here is a brief and simple example of how to use it:


// Indicate that we want to use XOAUTH2 SASL mechanism
vmime::security::sasl::SASLMechanismFactory::getInstance()->
    registerMechanism <vmime::security::sasl::XOAuth2SASLMechanism>("XOAUTH2");

// Create a new session
vmime::shared_ptr <vmime::net::session> sess = vmime::net::session::create();

// Use a custom authenticator to force using XOAUTH2 mechanism
vmime::shared_ptr <vmime::security::authenticator> xoauth2Auth =
    vmime::make_shared <vmime::security::sasl::XOAuth2SASLAuthenticator>
        (vmime::security::sasl::XOAuth2SASLAuthenticator::MODE_EXCLUSIVE);

// Create a new SMTPS service to GMail
vmime::shared_ptr <vmime::net::transport> tr = sess->getTransport(
    vmime::utility::url("smtps://smtp.gmail.com:465"), xoauth2Auth
);

tr->setProperty("options.need-authentication", true);
tr->setProperty("auth.username", "your-email@gmail.com");
tr->setProperty("auth.accesstoken", "ya29.5MEMlacTJifpYHHGn3V...your-access-token...kIOWy3wft5Rs");

tr->connect();

// Do whatever you want with 'tr' here!

In the previous example, if the XOAUTH2 authentication fails, no other authentication mechanism will be tried. If you want to fall back on basic username/password authentication mechanism if XOAUTH2 fails, simply replace MODE_EXCLUSIVE with MODE_SUGGEST. And don’t forget to also set the auth.password property!

For more information about XOAUTH2, see OAuth 2.0 mechanism on Google Developers page.

New feature: simultaneously get and fetch messages

We have released a new feature for IMAP stores: get and fetch messages in a single operation. This will avoid one round-trip to the server.

Let’s say we want to get the flags for the messages UID 2 to 6. Until today, we used to write the following code:

vmime::shared_ptr <vmime::net::folder> f = /* ... */;

vmime::net::messageSet mset(vmime::net::messageSet::byUID(2, 6));

vmime::net::fetchAttributes attribs;
attribs.add(vmime::net::fetchAttributes::FLAGS);

std::vector <vmime::shared_ptr <vmime::net::message> >
    msgs = f->getMessages(mset);

f->fetchMessages(msgs, attribs);

// ...do something with msg[n]->getFlags()...

This resulted into the following IMAP client-server dialog, with two FETCH commands in sequence:

C: a005 UID FETCH 2:6 UID                <-- getMessages()
S: * 1 FETCH (UID 2 MODSEQ (1268172))
S: * 2 FETCH (UID 3 MODSEQ (1268417))
S: * 3 FETCH (UID 4 MODSEQ (1268417))
S: * 4 FETCH (UID 5 MODSEQ (1268312))
S: * 5 FETCH (UID 6 MODSEQ (1268417))
S: a005 OK Success
C: a006 FETCH 1:5 (FLAGS)                <-- fetchMessages()
S: * 1 FETCH (MODSEQ (1268172) FLAGS (NotJunk $NotJunk \Seen))
S: * 2 FETCH (MODSEQ (1268417) FLAGS (NotJunk $NotJunk \Seen))
S: * 3 FETCH (MODSEQ (1268417) FLAGS (NotJunk $NotJunk \Seen))
S: * 4 FETCH (MODSEQ (1268312) FLAGS (NotJunk $NotJunk \Seen))
S: * 5 FETCH (MODSEQ (1268417) FLAGS (NotJunk $NotJunk \Seen))
S: a006 OK Success

With the new getAndFetchMessages() method, we can now write:

vmime::shared_ptr <vmime::net::folder> f = /* ... */;

vmime::net::messageSet mset(vmime::net::messageSet::byUID(2, 6));

vmime::net::fetchAttributes attribs;
attribs.add(vmime::net::fetchAttributes::FLAGS);

std::vector <vmime::shared_ptr <vmime::net::message> >
    msgs = f->getAndFetchMessages(mset, attribs);

// ...do something with msg[n]->getFlags()...

Here is now the dialog between the client and the server:

C: a006 UID FETCH 2:6 (FLAGS UID MODSEQ) 
S: * 1 FETCH (UID 2 MODSEQ (1268172) FLAGS (NotJunk $NotJunk \Seen))
S: * 2 FETCH (UID 3 MODSEQ (1268417) FLAGS (NotJunk $NotJunk \Seen))
S: * 3 FETCH (UID 4 MODSEQ (1268417) FLAGS (NotJunk $NotJunk \Seen))
S: * 4 FETCH (UID 5 MODSEQ (1268312) FLAGS (NotJunk $NotJunk \Seen))
S: * 5 FETCH (UID 6 MODSEQ (1268417) FLAGS (NotJunk $NotJunk \Seen))
S: a006 OK Success

That is, one round-trip to the server saved. Please note that this will work with any fetch attribute, and should also work with any IMAP-compliant server!

New feature: get parsed message from store

Hi VMime users!

Let’s focus today on a new feature in VMime 0.9. Until now, when you wanted to “explore” the contents of a vmime::net::message (that is, a message hosted on a IMAP or a POP3 store), you had to extract it entirely, or to use low-level fetch() and extract() functions on the message object.

Since the current development release of May 18th, there is a new helper function that will let you access the remote message like a “normal” vmime::message, and actually download the message data only when it is required (this only works for IMAP).

// Connect to the IMAP store
vmime::ref <vmime::net::session> sess = vmime::net::session::create();

vmime::utility::url storeURL("imap://username:password@imap.example.com");

vmime::ref <vmime::net::store> store = sess->getStore(storeURL);
store->connect();

// Open the INBOX
vmime::ref <vmime::net::folder> folder = store->getDefaultFolder();
folder->open(vmime::net::folder::MODE_READ_WRITE);

// Get the first message in the INBOX
vmime::ref <vmime::net::message> msg = f->getMessage(1);

// Construct the parsed message (a few data, header and structure,
// is actually downloaded from the IMAP server)
vmime::ref <vmime::message> parsedMsg = msg->getParsedMessage();

// Here, extract() will actually download message data from the server
vmime::utility::outputStreamAdapter out(std::cout);
parsedMsg->getBody()->extract(out);

That’s all folks! Update your source tree to the latest SVN head to enjoy this new feature.

Connecting to GMail SMTP/IMAP

Some users fequently ask me how to connect to GMail SMTP service with VMime. You have to connect to server using SMTP protocol (not SMTPS), and set the connection.tls property to true to initiate a secured connection using STARTTLS.

The following code is known to work:

vmime::utility::url url("smtp://smtp.gmail.com");
vmime::ref <vmime::net::transport> tr = session->getTransport(url);
tr->setProperty("connection.tls", true);
tr->setProperty("auth.username", "gmail-login");
tr->setProperty("auth.password", "gmail-password");
tr->setProperty("options.need-authentication", true);
tr->setCertificateVerifier(yourCertificateVerifier);

To connect to IMAP on GMail, use the following code:

vmime::utility::url url("imaps://login:password@imap.gmail.com:993");
vmime::ref <vmime::net::store> store = session->getStore(url);
store->setCertificateVerifier(yourCertificateVerifier);
store->connect();