Copy link to clipboard
I'm trying to architect a small feature which reds the full .cfmail files before they leave the server. The reading can be done either of a copy or the original, I just need to be able to read the contents of every single file that gets written to the spool directory.
Question: does the polling feature work flawlessly for this purpose, or is there a possibility of one/some getting removed by CF before the polled gateway can read it? And if, the latter are there any solid, production alternative approaches.
Thank you in advance.
Copy link to clipboard
As far as how quick the .cfmail files are processed, I'm not sure. However, there might be some leeway by using spoolenable="true" in the cfmail tag. Maybe that will keep the .cfmail files handy for the DirectoryWatcher to kick in before processing.
^ _ ^
Well, first, how long they remain in the spool folder is controlled by the CF Admin setting for the mail spool interval, which defaults to 15 seconds. (There may also be an app-level overrride for that, but I don't recall.)
As such, no, there's no way you can "guarantee" to capture every email sent out, by using the directorywatcher gateway. The interval is EVERY 15 seconds, so if an email was generated and spooled, the spool may be cleared before the dw gateway could capture it. I mean, it will PROBABLY be able to catch everyone, but no, there's no way to guarantee it.
And as for the spoolenable attribute, well, that (which defaults to true) is indeed what controls WHETHER the email is spooled. If it were set to false, it would not be but instead the cfmail tag using it would be run synchronously: it would not move on to the next tag until the smtp server received the email--and if the smtp server rejected it, the cfmail tag would fail.
What you might want to consider (if this is really manadatory to capture all email) would be to create your own cfmail custom tag (like cf_mail), and have THAT first log the email and THEN pass it to cfmail. I know it's unsatisfactory, as it involves code changes.
Sadly, no, CF does not offer a means to ovverride what CF tags do. (Actually, there are SOME CF tags that are implemented as CFM files, and one COULD try to override those by replacing the files found in cfusion\wwwroot\WEB-INF\cftags, but cfmail is not one of those.)
Maybe another way to look at this is to not ask CF to save off every email, but the smtp server instead. You can change that and never impact CF or CF code. Indeed, you could switch to another SMTP server that DID offer that feature and you'd need only change the CF Admin mail page's server value (or the cfmail SERVER attribute, if that's used to override the Admin setting).
Let us know how you get on with this idea of yours.
Thank you, Charlie, for the ever-thorough response! I'm heading back to the stakeholder to see if I can sell them on just doing our best to all-but-guarantee results. I'm thinking to extend the spooling to something grossly longer, like 5 minutes, and then set the gateway to poll ever second. I'll try to come back here and post my feedback and some test results.
fwiw, the directory watch idea was nixed on account of risk of losing data. just going to add a one-line function to every place (appropriate) where cfmail is called with the application.
I just thought of something. What about CC'ing a blind email address and using cfexchange to poll that folder? New emails can be processed, and then deleted from the folder. This _should_ ensure every email gets processed, without altering present code (other than adding the CC).
^ _ ^
Copy link to clipboard
Question: does the polling feature work flawlessly for this purpose
No, the polling feature using DirectoryWatcher is flawed. The thread that spools emails and the DirectoryWatcher are two, separate asynchronous processes. You want them to have simultaneous access to the files in the spool folder. This could create thread contention, irrespective of how long you set the spool interval.
Question: are there any solid, production alternative approaches
1) log the messages before sending them (Charlie's suggestion). For example:
<cfset emailStruct=structNew()> <cfset emailStruct.toField="toAddress@domain.com"> <cfset emailStruct.fromField="fromAddress@domain.com"> <cfset emailStruct.subject="The subject line"> <!--- emailBody contains the body of the e-mail ---> <cfset emailStruct.body=emailBody> <cfsavecontent variable="email" > <cfdump var="#emailStruct#"> </cfsavecontent> <cflog text="#email#" file="email" type="information"> <!--- The cfmail tag follows --->
2) Add an admin e-mail address - and, perhaps, also an e-mail supplied by the client - to the BCC in the cfmail tag. (WolfShade's suggestion, and my preferred solution)
And I'll throw in for good measure that I do agree that Wolfshade's idea of just bcc'ing an auditing address seems far better than changing code to log the emails. It does mean adding that to all cfmail tags (I am not aware of any way to force it to happen by configuration), but a benefit is that it would only get sent (to the audit address) if the communication out of CF to the SMTP server worked, whereas the logging would happen either way. Some might see the latter as its own benefit, of course.