I'm running qpsmtpd head (or thereabouts) async. Both Linux and Solaris. The behaviour I am reporting has _always_ been present through many previous iterations of qpsmtpd and "stock" spamassassin plugin. I'm only now trying to figure out how to get it swatted.
I'm not sure at this point where non-async has the same problem. There are a number of places in the plugins (eg: SpamAssassin, ClamAV, some plugins of my own) that insert headers using ...header->add(). Eg: (from config/plugins): ntm/spamassassin spamd_socket %%ROOT%%/spool/spamd.socket reject_threshold 6 leave_old_headers drop ntm/clamdscan deny_viruses yes clamd_socket %%ROOT%%/spool/clamd.socket scan_all yes [The %%ROOT%% thingies are expanded to the right place during rule push.] For some reason, SpamAssassin insists on inserting its headers at the _beginning_ of the header block, whereas all the others insert theirs at the end. Obviously, I'd prefer they all did it at the end. This is what spamassassin is doing: X-Spam-Level: ********* X-Spam-Status: Yes, hits=9.3 required=5.0 tests=ADVANCE_FEE_2,ADVANCE_FEE_3,FORGED_MUA_OUTLOOK,MISSING_HEADERS X-Spam-Flag: YES X-Spam-Check-By: ertps004.nortel.com Received: from static-96-254-76-2.tampfl.fios.verizon.net (HELO SRV1.tuscanypres) by ertps004.nortel.com (qpsmtpd/0.43rc1) with ESMTP; Mon, 20 Oct 2008 10:24: 48 -0400 <rest of header> [That received line is the one _I_ insert] The changes I've done to the stock spamassassin plugin are (all in data_post hook): 1) return DECLINED (without SA scan) if a transaction->note() indicates that a previous plugin has determined the email is going to be blocked (or that filters are not to be run on the email). 2) if spamassassin triggers on the email, set the same transaction->note(), and return DECLINED. The transaction note() is essentially the "reason for blocking" code that's logged in a later plugin that actually does the DENY. clamdscan has been altered in the same way. The plugin has _not_ been altered for async. In the meantime, the clamdscan (which has been altered in the same way) inserts its headers properly (at the end of the headers).