Today I finally found the solution.
In my early searches, Google had nothing useful to say, so I'm writing this post in the hopes of helping some future PHP programmer who's banging his or her head against a wall in angst.
Here's the setup: for our online back-to-school / class registration system at Minnehaha (that I wrote), there's a PHP script that allows users to reset their forgotten passwords. Part of this process involves sending them an email with a unique identifier. The problem is that email wasn't always being delivered: users with visi.com email addresses did not receive their message. After examining the web server's logs, I discovered the error message above, something about a malformed MIME header.
I emailed Red Condor, Visi's filtering service (their name showed up in the log next to the bounce notice), and they wrote back in about 10 minutes. Literally. I was floored at how quickly their tech support responded.
Our first place of investigation was my custom headers. I need these so that the message is sent as HTML instead of plan text, but since it's human-made, it's the most logical place for a mistake. The PHP code looks something like this:
$headers = 'MIME-Version: 1.0'."\n".
'From: Name <email@example.com>'."\n";
One possible culprit was the line endings. The mail message spec dictates that all lines in the header need to end with
\r\n(carriage return and a new line. For those too young to know what a carriage return is, Google for "typewriter"; it's an old-school laptop our parents and grandparents grew up using). If you'll notice, in my code above, I only have
\r\n. IMPORTANT NOTE: If you're running PHP on a UNIX or Mac OS X server, the
mail()command will add
\rfor you! So if you manually put in
\r\n, you'll end up getting
\r\r\n, which is bad.
So as far as I could tell, my headers were all correct.
Next, I manually grabbed my entire inbox from the mail server and examined it in a hex editor (Mac OS X users I recommend a free program called Hex Fiend). All my line endings were as they should be (Hex values
I emailed back and forth a few times with Red Condor, and my tech support person suggested I capture the email coming from the server before it gets to the mail server, and then send them those bytes. I spent a day trying to figure out how to get PHP to dump an email into a file before giving up. I thought I'd never find the solution, until...
RC tech support suggested I try a packet capture.
Well that sounds fun. I know the concept, and I found a free program (CocoaPacketAnalyzer) to let me do it easily, but could I really find what I was looking for?
Yes, turns out.
I started a capture on the webserver, told the script to send an email to one of my Visi users, and then examined the packets that were sent. After a little searching I found the packet with the actual mail message, and here's the gold: all the bytes were correct except for the
X-PHP-Originating-Scriptheader, which had the dreaded
How the heck do you get rid of that? It's not an error in my custom code, it's an error in PHP's
Our network consultant Dan suggested I disable that header to see if that would solve Red Condor's rejection issue.
Google was helpful on this front: you can disable the
X-PHP-Originating-Scriptheader by setting
mail.add_x_header = OFFin your
php.inifile (and then restarting web services, obviously).
Turns out, disabling that header ALSO removed the extra
\r. In my next packet capture, the extra byte was GONE and the email sent successfully, no more bouncing.
It's always something so small, so innocent. I don't know if Red Condor was rejecting the
X-PHP-Originating-Scriptheader or was just unhappy about the extra carriage return byte, but either way, I'm happy it's solved!