DevLearningTools

MODULE 13 · LESSON 08

<cfimap>

Reading and managing mail on an IMAP server with cfimap: a persistent connection pattern, its actions, cfimapfilter for server-side filtering, saving attachments, and Lucee's near-identical implementation.

New lessons are added one at a time as the course gets built out — a graded quiz for each lesson is still on the way.

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"

NOTE

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

Tag Syntax
<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

AttributeMeaning
actionThe operation to perform, defaults to getHeaderOnly, not getAll
connectionThe named session, required for open/close and reused by every other action
server / username / passwordIMAP server address and credentials (required for open)
secure / portSSL and port (143 for non-secure, 993 for secure)
folderThe target mailbox folder, defaults to INBOX
nameThe query result variable name (getAll, getHeaderOnly, listAllFolders)
attachmentPathRequired with getAll if attachments should actually be saved to disk
messageNumber / uidTargets specific message(s) instead of the whole folder
startRow / maxRowsPagination over a folder's messages

All 11 Actions

actionDoes
open / closeEstablishes or ends the persistent connection
getHeaderOnlyRetrieves message headers only (the default)
getAllRetrieves full messages, including body and attachments
deleteRemoves specified messages
markReadMarks messages as read
moveMailMoves messages into another folder (newFolder)
createFolder / deleteFolder / renameFolderManages mailbox folders
listAllFoldersLists existing folders (recurse for subfolders)

What's in the Query Result

actionColumns
getHeaderOnlyANSWERED, CC, DELETED, DRAFT, FLAGGED, FROM, HEADER, LINES, MESSAGEID, MESSAGENUMBER, RECENT, REPLYTO, RXDDATE, SEEN, SENTDATE, SIZE, SUBJECT, TO, UID
getAllEverything from getHeaderOnly, plus ATTACHMENTFILES, ATTACHMENTS, BODY, CIDS, HTMLBODY, TEXTBODY
listAllFoldersFULLNAME, NAME, NEW, TOTALMESSAGES, UNREAD

A Real Example: Fetching Full Messages With Attachments

Tag Syntax
<cfimap
    action="getAll"
    connection="myInbox"
    folder="INBOX"
    name="fullMessages"
    attachmentPath="#expandPath('./received-attachments/')#"
    generateUniqueFileNames="true"
    maxRows="10">
NOTE

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

FilterAttribute UsedMeaning
subjectvalueMatches subject line content
from / tovalueMatches sender or recipient
flagvalueMatches a message flag: ANSWERED, DELETED, DRAFT, NEW, FLAGGED, RECENT, OLD, SEEN, UNANSWERED, UNDELETED, UNDRAFT, UNSEEN, UNFLAGGED
timeReceived / timeSentfrom / toA date/time range (mutually exclusive with value)
NOTE

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

Tag Syntax
<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

Tag Syntax
<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.