So CFX_POP3 seems to have a limited life left in it as it is a 32-bit CFX tag written in Delphi and masquerading as a C++ tag for the purposes of talking to ColdFusion. It seems that a 64-bit compiler for Delphi is quite a way off even now but ColdFusion developers are already moving onto a 64-bit CF platform rendering CFX_POP3 useless.
To that end, I figured I'd start looking at extending CF using built in functions and the available Java classes to see if I could extend the functionality available in CFPOP so we no longer need CFX_POP3.
I guess this little project will end up being a CFC but for now I thought I'd try to re-create one of the more useful features of CFX_POP3; retrieving just a list of UID values. By retrieving just a UID list from the server we can speed up several processes and it is one of the reasons why CFX_POP3 has been so popular in the past.
So this morning I set about looking at the available POP3 functions in the java class files that ship with CF8 and it seems that the javax mail classes are chock full of excellent toys.
The function to retrieve a list of UID's from the POP3 server needs to be quick otherwise we may as well use CFPOPs "getHeadersOnly" and the javax mail classes provide a way of pre-fetching UIDs using a FetchProfile class and adding in a FetchProfileItem to specify that you want to retrieve specific items from the POP3 server.
The FetchProfileItem class is an "Inner Class" of the UIDFolder interface and creating that object is a little bit different from creating an object from what would be classed as the "Outer Class".
<cfset var fpItem = createObject("java", "javax.mail.UIDFolder$FetchProfileItem")>
Note the reference to FetchProfileItem is predicated with the outer class javax.mail.UIDFolder and the use of the $ symbol indicates that the class is an Inner class to be instantiated.
Once I'd figured out that little bit of code, it was a simple process of accessing mailbox and using the folder.fetch function to retrieve the UID values of the messages on the server.
On my tests with the function, using CFPOP to retrieve the headers of the mails in a mailbox with 2031 messages in it, it took on average 80 seconds to retrieve the details including the UID values.
Using my new pure CF/Java function to retrieve just the UID values for all the mails in the same mailbox took an average of 0.42 seconds. Yes you read that right, it took on average under half a second to retrieve the same number of UID values from the server.
So, without further ado, here is the code that retrieves UID values from a POP3 mailbox in CF.
<cffunction name="getPOPUIDList" access="public" returntype="array" output="false"
hint="Retrieves an array of UID values for the e-mails in an account">
<cfargument name="server" type="string" required="true">
<cfargument name="username" type="string" required="true">
<cfargument name="password" type="string" required="true">
<cfargument name="port" type="numeric" required="false" default="110" hint="POP3 port to use">
<cfargument name="timeout" type="numeric" required="false" default="30000" hint="Timeout in ms">
<cfset var javaSystem = createObject("java", "java.lang.System")>
<cfset var javaProps = javaSystem.getProperties()>
<cfset var javaSession = createObject("java", "javax.mail.Session")>
<cfset var fp = createObject("java", "javax.mail.FetchProfile")>
<cfset var fpItem = createObject("java", "javax.mail.UIDFolder$FetchProfileItem")>
<cfset var folder = "">
<cfset var UIDs = ArrayNew(1)>
<cfset var mailMsg = 1>
<cfset var pop3Session = "">
<cfset var store = "">
<cfset var messages = "">
<cfset javaProps.setProperty("mail.pop3.port", arguments.port)>
<cfset javaProps.setProperty("mail.pop3.connectiontimeout", arguments.timeout)>
<cfset pop3Session = javaSession.getDefaultInstance(javaProps)>
<cfset store = pop3Session.getStore("pop3")>
<cftry>
<cfset store.connect(arguments.server, arguments.username, arguments.password)>
<cfcatch>
<!--- if we're here throw a useful error --->
<cfthrow message="Unable to connect to #arguments.server#."
detail="The most common reason for this is a mis-spelling of one of the arguments.">
</cfcatch>
</cftry>
<cfif store.isConnected()>
<cftry>
<!--- for the POP3 protocol the folder is always "INBOX" --->
<cfset folder = store.getFolder("INBOX")>
<!--- open the folder --->
<cfset folder.open(1)>
<!--- create the message objects --->
<cfset messages = folder.getMessages()>
<!--- set up the filter with the FetchProfile, adding the FetchProfileItem of the UID --->
<cfset fp.add(fpItem.UID)>
<cfset folder.fetch(messages, fp)>
<!--- loop through the retrieved message objects and retrieve the UID from the object appending it to the result array --->
<cfloop from="1" to="#ArrayLen(messages)#" index="mailMsg">
<cfset ArrayAppend(UIDs, folder.getUID(messages[mailMsg]))>
</cfloop>
<!--- close the connection to the server --->
<cfset store.close()>
<cfcatch>
<!--- if we're here, close the connection to the server if necessary --->
<cfif store.isConnected()>
<cfset store.close()>
</cfif>
</cfcatch>
</cftry>
<cfelse>
<cfthrow message="Unable to connect to #arguments.server#."
detail="The most common reason for this is a mis-spelling of one of the arguments.">
</cfif>
<cfreturn UIDs>
</cffunction>
Then we call the function as easy as this...
<cfset myUIDList = getPOPUIDList(myServer, myUsername, myPassword)>
If we need to set the port and timeout for the connection, we can do as follows
<cfset myUIDList = getPOPUIDList(myServer, myUsername, myPassword, 110, 30000)>
You can download the code here.