The previous lessons covered sending mail. cfimap covers the other direction: reading and managing mail already sitting on an IMAP server, retrieving headers or full messages, filtering server-side, and organizing folders, directly from CFML.
Learning Objectives
After completing this lesson, you'll be able to:
- Open a persistent IMAP connection and retrieve message headers.
- Fetch full messages, including attachments, with getAll.
- Filter results on the server with cfimapfilter instead of pulling everything and filtering in CFML.
- Move and organize mail between folders.
The Persistent Connection Pattern
action="open"
Connection Reused Across Calls
getHeaderOnly / getAll / moveMail...
action="close"
This is the same open/reuse/close shape covered for cfftp, a named connection is opened once and passed to every subsequent cfimap call.
A Basic Connection and Header Fetch
<cfimap action="open" connection="myInbox" server="imap.example.com" username="me@example.com" password="#imapPassword#" secure="true" port="993"> <cfimap action="getHeaderOnly" connection="myInbox" name="messages" folder="INBOX" maxRows="25"> <cfimap action="close" connection="myInbox">
cfimap's Core Attributes
| Attribute | Meaning |
|---|---|
| action | The operation to perform, defaults to getHeaderOnly, not getAll |
| connection | The named session, required for open/close and reused by every other action |
| server / username / password | IMAP server address and credentials (required for open) |
| secure / port | SSL and port (143 for non-secure, 993 for secure) |
| folder | The target mailbox folder, defaults to INBOX |
| name | The query result variable name (getAll, getHeaderOnly, listAllFolders) |
| attachmentPath | Required with getAll if attachments should actually be saved to disk |
| messageNumber / uid | Targets specific message(s) instead of the whole folder |
| startRow / maxRows | Pagination over a folder's messages |
All 11 Actions
| action | Does |
|---|---|
| open / close | Establishes or ends the persistent connection |
| getHeaderOnly | Retrieves message headers only (the default) |
| getAll | Retrieves full messages, including body and attachments |
| delete | Removes specified messages |
| markRead | Marks messages as read |
| moveMail | Moves messages into another folder (newFolder) |
| createFolder / deleteFolder / renameFolder | Manages mailbox folders |
| listAllFolders | Lists existing folders (recurse for subfolders) |
What's in the Query Result
| action | Columns |
|---|---|
| getHeaderOnly | ANSWERED, CC, DELETED, DRAFT, FLAGGED, FROM, HEADER, LINES, MESSAGEID, MESSAGENUMBER, RECENT, REPLYTO, RXDDATE, SEEN, SENTDATE, SIZE, SUBJECT, TO, UID |
| getAll | Everything from getHeaderOnly, plus ATTACHMENTFILES, ATTACHMENTS, BODY, CIDS, HTMLBODY, TEXTBODY |
| listAllFolders | FULLNAME, NAME, NEW, TOTALMESSAGES, UNREAD |
A Real Example: Fetching Full Messages With Attachments
<cfimap
action="getAll"
connection="myInbox"
folder="INBOX"
name="fullMessages"
attachmentPath="#expandPath('./received-attachments/')#"
generateUniqueFileNames="true"
maxRows="10">attachmentPath is required for getAll if attachments are meant to actually be written to disk. generateUniqueFileNames avoids one message's attachment silently overwriting another's with the same filename.
cfimapfilter: Filtering on the Server
| Filter | Attribute Used | Meaning |
|---|---|---|
| subject | value | Matches subject line content |
| from / to | value | Matches sender or recipient |
| flag | value | Matches a message flag: ANSWERED, DELETED, DRAFT, NEW, FLAGGED, RECENT, OLD, SEEN, UNANSWERED, UNDELETED, UNDRAFT, UNSEEN, UNFLAGGED |
| timeReceived / timeSent | from / to | A date/time range (mutually exclusive with value) |
cfimapfilter only applies inside a cfimap action="getall" block, it filters on the IMAP server itself rather than pulling every message and filtering afterward in CFML.
A Real Example: Unread Messages About a Specific Topic
<cfimap action="getall" connection="myInbox" name="unreadMeetingEmails" folder="INBOX">
<cfimapfilter name="flag" value="UNSEEN">
<cfimapfilter name="subject" value="meeting">
</cfimap>A Real Example: Moving Processed Mail Out of the Inbox
<cfimap action="moveMail" connection="myInbox" folder="INBOX" newFolder="Processed" uid="#messages.uid#">
Lucee: Nearly Identical to cfpop
Lucee's own documentation states its imap tag "works exactly the same way as the cfpop tag," and since Lucee 7.1, mail functionality (both cfmail and cfimap) moved into a separate Mail extension, included in the full Lucee JAR distribution rather than the core install.
Common Beginner Mistakes
Assuming action defaults to getAll
It defaults to getHeaderOnly. A call expecting the message body or attachments without explicitly setting action="getall" gets only headers back.
Calling getAll without attachmentPath, expecting attachments to be saved
attachmentPath is required for getAll if attachments should actually be written to disk, without it they aren't saved.
Filtering in CFML after pulling every message instead of using cfimapfilter
cfimapfilter runs the filter on the IMAP server itself, pulling only matching messages, rather than transferring everything and discarding most of it in CFML.
Not closing the connection after a batch of operations
The persistent connection should be explicitly closed with action="close" once the batch of calls that reuse it is done.
Best Practices
- Open a persistent connection once and reuse it across multiple cfimap calls, rather than reopening for each one.
- Filter with cfimapfilter on the server instead of pulling every message and filtering in CFML.
- Use generateUniqueFileNames when saving attachments from more than one message, to avoid silent overwrites.
- Reach for getAll deliberately, it transfers full message bodies and attachments, not just headers, it costs real time and bandwidth compared to getHeaderOnly.
Interview Questions
What does cfimap's action attribute default to?
getHeaderOnly, not getAll. Retrieving a message's body or attachments requires explicitly setting action="getall".
Why use cfimapfilter instead of filtering a getAll result in CFML afterward?
cfimapfilter runs the filter on the IMAP server itself, only matching messages are actually transferred, rather than pulling everything and discarding most of it locally.
What's required for cfimap to actually save a message's attachments to disk?
attachmentPath must be set on a getAll call, without it attachments aren't written out even though the query result may reference them.
How does Lucee's imap tag compare to its pop tag?
Lucee's own documentation states it works exactly the same way as cfpop, and since Lucee 7.1 both live in a separate Mail extension rather than the core install.
Summary
In this lesson, you opened a persistent IMAP connection, retrieved headers and full messages with attachments, filtered results on the server with cfimapfilter, moved mail between folders, and covered Lucee's near-identical implementation shared with cfpop.
What's Next?
The next lesson covers cfpop, reading mail over the older POP3 protocol.