An IMAP Client for R
mRpostman is a session-based IMAP client that implements the full command sets of the IMAP4rev2 (RFC 9051) and IMAP4rev1 (RFC 3501) protocols, along with the optional extensions registered with the Internet Assigned Numbers Authority (IANA), allowing you to perform virtually all e-mail operations from within R. The aim of this package is to pave the way for email data analysis in R. To do so, mRpostman makes extensive use of the {curl} package and the libcurl C library. Since version 3.0.0, searches are written as plain R expressions through query(), and every method returns a value ready for the analysis stage.
mRpostman’s official website: https://allanvc.github.io/mRpostman/
Cite mRpostman: A. V. C. Quadros, “mRpostman: An IMAP Client for R”, Journal of Open Research Software, vol. 12, no. 1, p. 4, 2024, doi: 10.5334/jors.480. http. Refer to citation("mRpostman").
Version 3.0.0 is the product of a deep refactoring of the package, and a few changes are not backward compatible:
use_uidnow defaults toTRUE: every method operates on message UIDs instead of sequence numbers by default. UIDs are stable identifiers (sequence numbers are renumbered whenever messages are expunged), so ids remain valid across sessions — but scripts that relied on sequence numbers under the old default must now passuse_uid = FALSE(once, atconfigure_imap(), if preferred).- R >= 4.1.0 is now required.
- The fourteen
search_*()methods and theAND()/OR()helpers are soft-deprecated: searches are now written as plain R expressions throughquery(), or with the criteria constructors combined by the native&,|, and!operators. - The old attachment entry points (
fetch_attachments(),fetch_attachments_list(),get_attachments(),list_attachments()) are also soft-deprecated, replaced byattachments(),attachments_manifest(), andextract_attachments(). - The
name,to_folder, andmsg_uidarguments were unified asfolderandmsg_idthroughout the API; the old names still work but warn once per session. use_uid,mute, andretriescan now be set once, inconfigure_imap(), as connection-level defaults.
Nothing was removed: the deprecated spellings keep working and emit lifecycle warnings pointing to their replacements. See NEWS.md for the complete list of changes.
| Provider | IMAP Server |
|---|---|
| Gmail | imap.gmail.com |
| Office 365 | outlook.office365.com* |
| Outlook.com (Hotmail and Live.com) | imap-mail.outlook.com |
| Yahoo Mail | imap.mail.yahoo.com |
| iCloud Mail | imap.mail.me.com |
| AOL Mail | imap.aol.com |
| Zoho Mail | imap.zoho.com |
| Yandex Mail | imap.yandex.com |
| GMX Mail | imap.gmx.com |
| Mail.com | imap.mail.com |
| FastMail | imap.fastmail.com |
* For Office 365 accounts, the username should be set as user@yourcompany.com or user@youruniversity.edu for example.
From version 0.9.0.0 onward, mRpostman is implemented under the OO paradigm, based on an R6 class called ImapCon. Its derived methods, and a few independent functions enable the R user to perform a myriad of IMAP commands.
The main groups, in the shape the package has since 3.0.0:
- connection:
configure_imap()(including the connection-level defaultsuse_uid,mute, andretries),disconnect(), thereset_*()setters; - search:
query(), an ordinary R expression such ascon$query((subject == "budget" | "budget 3") & flag != "SEEN");search()with the criteria constructors (string(),before(),flag(),verbatim(), …) combined by the native&,|, and!; the oldersearch_*()methods andAND()/OR()keep working as deprecated spellings; - fetch and decoding:
fetch_body(),fetch_header(),fetch_text(),fetch_metadata(),fetch_envelope(),fetch_bodystructure(), and the decodersclean_msg_text()anddecode_mime_header(); - attachments:
attachments_manifest()(list without downloading),attachments()(download, guided by theBODYSTRUCTURE), andextract_attachments()for already-fetched messages; - mailbox management: folder listing/creation/renaming/status, flags, copy/move/delete,
append_msg()/append_msgs(), expunge; - server-side computation and events:
sort(),thread(), theesearch_*()aggregates,idle()andnotify()on a dedicated raw-socket route, plus every capability extension registered with IANA (quota, ACL, metadata, CONDSTORE/QRESYNC, and the rest).
The IMAP protocol has a mandatory core — the IMAP4rev1 commands defined in RFC 3501, revised and consolidated by IMAP4rev2 in RFC 9051, which every compliant server must implement — plus a set of optional extensions, each advertised by the server in its CAPABILITY response. mRpostman covers both. For the extension-based methods, mRpostman checks the server’s advertised capabilities and, if the required one is missing, raises an informative error instead of letting the server reply with a cryptic BAD Unknown command. You can inspect what your server supports with list_server_capabilities().
| IMAP command | mRpostman method(s) |
|---|---|
CAPABILITY |
list_server_capabilities() |
NOOP |
noop() |
CHECK |
check() |
LOGIN / AUTHENTICATE |
configure_imap() |
LOGOUT |
disconnect() |
SELECT / EXAMINE |
select_folder() / examine_folder() |
CREATE / DELETE / RENAME |
create_folder() / delete_folder() / rename_folder() |
SUBSCRIBE / UNSUBSCRIBE |
subscribe_folder() / unsubscribe_folder() |
LIST / LSUB |
list_mail_folders() / list_subscribed_folders() |
STATUS |
status() |
APPEND |
append_msg() |
SEARCH |
search(), search_before(), search_since(), search_string(), … (all search_*) |
FETCH |
fetch_body(), fetch_header(), fetch_text(), fetch_metadata(), fetch_attachments() |
STORE |
add_flags(), remove_flags(), replace_flags() |
COPY |
copy_msg() |
CLOSE |
close_folder() |
EXPUNGE |
expunge(), delete_msg() |
| IMAP command | mRpostman method(s) |
Capability | RFC |
|---|---|---|---|
SORT |
sort() |
SORT |
5256 |
THREAD |
thread() |
THREAD=REFERENCES / THREAD=ORDEREDSUBJECT / THREAD=REFS |
5256 |
GETQUOTA / GETQUOTAROOT |
get_quota() / get_quota_root() |
QUOTA |
9208 |
NAMESPACE |
namespace() |
NAMESPACE |
2342 |
ID |
id() |
ID |
2971 |
UNSELECT |
unselect_folder() |
UNSELECT |
3691 |
LIST (special-use) |
list_special_use_folders() |
SPECIAL-USE |
6154 |
MOVE |
move_msg() |
MOVE |
6851 |
SEARCH RETURN (ESEARCH) |
search(esearch = TRUE), esearch_count(), esearch_min_id(), esearch_max_id() |
ESEARCH |
4731 |
UID EXPUNGE, APPENDUID / COPYUID |
expunge(msg_uid = ...), append_msg() (returns the UID), copy_msg() / move_msg() ("copyuid" attribute) |
UIDPLUS |
4315 |
LIST ... RETURN (STATUS ...) |
list_folders_status() |
LIST-STATUS |
5819 |
SETQUOTA |
set_quota() |
QUOTA |
9208 |
GETACL / SETACL / DELETEACL / LISTRIGHTS / MYRIGHTS |
get_acl() / set_acl() / delete_acl() / list_rights() / my_rights() |
ACL |
4314 |
ENABLE |
enable() |
ENABLE |
5161 |
SEARCH RETURN (SAVE) |
search(save = TRUE), then msg_id = "$" in fetch/flag/copy/move/delete methods |
SEARCHRES |
5182 |
SORT RETURN (...) |
sort(return = ...) |
ESORT |
5267 |
LIST ... RETURN (CHILDREN SUBSCRIBED SPECIAL-USE) |
list_mail_folders(detailed = TRUE) |
LIST-EXTENDED |
5258 |
STATUS (SIZE) |
status(items = "SIZE"), list_folders_status(items = "SIZE") |
STATUS=SIZE |
8438 |
FETCH (PREVIEW) |
fetch_preview(), fetch_metadata(attribute = "PREVIEW") |
PREVIEW |
8970 |
FETCH (SAVEDATE), SEARCH SAVEDBEFORE/SAVEDON/SAVEDSINCE |
fetch_metadata(attribute = "SAVEDATE"), saved_before() / saved_on() / saved_since() |
SAVEDATE |
8514 |
SELECT (CONDSTORE), STATUS (HIGHESTMODSEQ), FETCH (MODSEQ), FETCH ... (CHANGEDSINCE), STORE ... (UNCHANGEDSINCE), SEARCH MODSEQ |
select_folder(condstore = TRUE), status(items = "HIGHESTMODSEQ"), fetch_metadata(attribute = "MODSEQ", changed_since = ), add_flags()/replace_flags()/remove_flags() (unchanged_since = ), modseq() |
CONDSTORE |
7162 |
SELECT (QRESYNC ...), UID FETCH ... (CHANGEDSINCE VANISHED) |
resync_folder(), fetch_changes() |
QRESYNC |
7162 |
GETMETADATA / SETMETADATA |
get_metadata() / set_metadata() |
METADATA |
5464 |
IDLE |
idle() (on a dedicated second connection) |
IDLE |
2177 |
APPEND (multiple literals) |
append_msgs() |
MULTIAPPEND |
3502 |
NOTIFY SET / NOTIFY NONE |
notify() |
NOTIFY |
5465 |
FETCH (BINARY.PEEK[...]) |
fetch_binary() |
BINARY |
3516 |
APPEND ... CATENATE |
append_catenate(), imap_url() |
CATENATE |
4469 |
COMPRESS DEFLATE |
compress = TRUE in the raw-socket methods |
COMPRESS=DEFLATE |
4978 |
LIST ... RETURN (MYRIGHTS) |
list_mail_folders(detailed = TRUE) (my_rights column) |
LIST-MYRIGHTS |
8440 |
CREATE ... (USE (...)) |
create_folder(special_use = ...) |
CREATE-SPECIAL-USE |
6154 |
AUTHENTICATE OAUTHBEARER |
configure_imap(oauth_mechanism = "OAUTHBEARER") |
AUTH=OAUTHBEARER |
7628 |
SORT (display keys) |
sort(by = "DISPLAYFROM"/"DISPLAYTO") |
SORT=DISPLAY |
5957 |
SEARCH RETURN (PARTIAL m:n) |
esearch_partial() |
PARTIAL / CONTEXT=SEARCH |
9394 / 5267 |
SORT RETURN (PARTIAL m:n) |
esort_partial() * |
CONTEXT=SORT |
5267 |
REPLACE |
replace_msg() * |
REPLACE |
8508 |
FETCH (EMAILID THREADID), STATUS (MAILBOXID) |
fetch_objectid() *, status(items = "MAILBOXID") * |
OBJECTID |
8474 |
UIDBATCHES |
uid_batches() * |
UIDBATCHES |
10022 |
ESEARCH IN (...) |
esearch_multi() * |
MULTISEARCH |
7377 |
UNAUTHENTICATE |
unauthenticate() * |
UNAUTHENTICATE |
8437 |
LANGUAGE / COMPARATOR |
language() * / comparator() * |
LANGUAGE / I18NLEVEL=2 |
5255 |
GENURLAUTH / URLFETCH |
genurlauth() * / urlfetch() * |
URLAUTH |
4467 |
CONVERT |
fetch_convert() * |
CONVERT |
5259 |
FETCH ANNOTATION / STORE ANNOTATION |
fetch_annotation() * / store_annotation() * |
ANNOTATE-EXPERIMENT-1 |
5257 |
SEARCH ... FUZZY |
fuzzy() criterion modifier * |
SEARCH=FUZZY |
6203 |
SEARCH ... FILTER |
filter_stored() criterion * |
FILTERS |
5466 |
APPEND size guard, STATUS (APPENDLIMIT) |
automatic in append_msg()/append_msgs(); status(items = "APPENDLIMIT") |
APPENDLIMIT |
7889 |
| non-synchronizing literals | automatic on the raw-socket methods | LITERAL+ / LITERAL- |
7888 |
Every capability registered with IANA is covered. The methods marked with an asterisk (*) are experimental: they follow the RFC grammars, but no widely deployed server advertises those capabilities (they are rare, brand new, or were never adopted), so they could not be exercised against a live server. Availability of the others varies by provider: Gmail, for instance, supports every non-experimental extension above except SORT and THREAD, which it has never implemented; to exercise sort() and thread() you need a server that advertises them (e.g. Dovecot-based hosts, Yandex, or Outlook/Office 365). Announcement-only capabilities (AUTH=, LOGINDISABLED, RIGHTS=, QUOTA=, APPENDLIMIT=n, MESSAGELIMIT=/SAVELIMIT=, IMAPSIEVE=, JMAPACCESS, INPROGRESS, CHILDREN, I18NLEVEL=1, referrals) require no dedicated command and are honored where they matter (folder listings, appends, error reporting). UIDONLY (RFC 9586) responses (UIDFETCH) are understood after enable("UIDONLY"). The raw-socket methods (idle(), notify(), append_msgs(), append_catenate(), fetch_binary(), replace_msg(), esearch_multi(), urlfetch(), fetch_convert()) need an imaps:// URL for TLS, since STARTTLS is not available on that connection.
# CRAN version
install.packages("mRpostman")
# Dev version
if (!require('remotes')) install.packages('remotes')
remotes::install_github("allanvc/mRpostman")mRpostman is built on the libcurl C library and requires libcurl >= 7.58.0 (>= 7.65.0 for OAuth2.0 authentication) — any recent Linux distribution satisfies this through libcurl4-openssl-dev (deb) or libcurl-devel (rpm); the macOS and Windows R builds already ship a suitable version.
The package ships a disposable local IMAP server (Dovecot, in a Docker container) plus a deterministic synthetic corpus generator, so every feature can be exercised offline and reproducibly — no credentials, OAuth2 setup, or provider rate limits involved:
# after starting the container (see the "sandbox" vignette):
con <- configure_imap(url = "imap://localhost:1430", username = "testuser",
password = "sandbox", use_ssl = FALSE)
populate_sandbox(con, n = 200) # uploads the corpus with the package's own APPENDReal data works too: ingest_maildir() uploads any local maildir-style directory to the server via APPEND, and enron_sandbox() builds on it to download (once, with consent, cached) the public Enron corpus and ingest a subset selected by custodian, folder, and date — turning the sandbox into a full e-mail data-analysis laboratory.
See the “A reproducible IMAP sandbox with Docker” vignette for the guided tour. The sandbox’s Dovecot server also advertises SORT and THREAD, making it a convenient place to try the extensions your provider may lack.
library(mRpostman)
con <- configure_imap(url = "imaps://outlook.office365.com",
username = "your_user@company.com",
password = rstudioapi::askForPassword())
# works with any provider from the table above (Gmail, Yahoo, Yandex, ...) and
# with self-hosted servers; use_uid, mute, and retries can also be set here,
# once, as connection-level defaults
con$list_server_capabilities()Most providers no longer accept plain account passwords from third-party apps: you will typically authenticate with OAuth2.0 (the xoauth2_bearer argument) or with an app-specific password generated in your account’s security settings. The “Using IMAP OAuth2.0 authentication in mRpostman” vignette covers the OAuth2.0 setup for Gmail and other providers.
con$select_folder("INBOX")
# subject contains "budget" or "budget 3", and the message is unread
ids <- con$query((subject == "budget" | "budget 3") & flag != "SEEN")
# sent in Q4 2021 and larger than 5 MB
ids <- con$query(sent >= "2021-10-01" & sent < "2022-01-01" & size > 5e6)Fields cover subject, from, to, body, text, flag, size, age, the date families, modseq, and header("Name"); see ?query for the full table. Raw protocol fragments enter through verbatim(), as in con$query(verbatim('X-GM-RAW "has:attachment"') & flag != "SEEN") on Gmail. The pre-3.0.0 search_*() methods keep working as deprecated spellings.
texts <- con$query(subject == "invoice" & age < 30 * 86400) %>%
con$fetch_text() %>%
clean_msg_text() # transfer encoding and charset decoded, plain character vectorids <- con$query(size > 1e6)
con$attachments_manifest(ids) # list without downloading
con$attachments(ids, dest = "~/att") # download, one folder per messageextract_attachments() does the same extraction offline, from messages already fetched with fetch_body().
Server-side sort() and thread(), the esearch_*() aggregates, idle()/notify() push notifications, quota, ACL, and the other IANA extensions are all methods on the same object; the basics vignette walks the full surface, and the sandbox vignette lets you try everything against a local disposable server, no account needed.
- a companion package with an interactive front-end for query building;
- eliminate the {stringr} dependency in REGEX;
- implement a progress bar in fetch operations;
-
search results truncation: This is a libcurl’s known bug which causes the search results to be truncated when there is a large number of message ids returned. To circumvent this problem, you can set a higher
buffersizevalue, increasing the buffer capacity, andverbose = TRUEfor monitoring the server response for truncated results when executing a search. When possible,mRpostmantries to issue a warning for possible truncated values. -
verbose = TRUEmalfunction on Windows: This seems to be related to the {curl} R package. When using theverbose = TRUEon Windows, the flow of information between the IMAP server and the R session presents an intermittent behavior, which causes it to not be shown on the console, or with a considerable delay. -
shared mailbox access not working: This seems to be another libcurl’s bug, although more tests need to be done to confirm it. It does not allow the user to connect to a shared mailbox. To circumvent this, if the shared mailbox has a password associated with it, you can try a direct regular connection.
-
xoauth2_bearerSASL error: This is related to old libcurl’s versions which causes the access token to not be properly passed to the server. This bug was fixed in libcurl 7.65.0. The problem is that many Linux distributions, such as Ubuntu 18.04, still provide libcurl 7.58.0 in their official distribution (libcurl4-openssl-dev). If you use a newer Linux distro such as Ubuntu 20.04, you should be fine as the distributed libcurl’s version will be above 7.65.0. Another alternative is to use plain authentication instead of OAuth2.0.
This package is licensed under the terms of the GPL-3 License.
Crispin, M. (2003), INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1, RFC 3501, March 2003, http.
Heinlein, P. and Hartleben, P. (2008). The Book of IMAP: Building a Mail Server with Courier and Cyrus. No Starch Press. ISBN 978-1-59327-177-0.
Ooms, J. (2020), curl: A Modern and Flexible Web Client for R. R package version 4.3, http.
Quadros, A. V. C. mRpostman: An IMAP Client for R, Journal of Open Research Software, vol. 12, no. 1, p. 4, 2024, doi: 10.5334/jors.480. http.
Stenberg, D. Libcurl - The Multiprotocol File Transfer Library, http.
