Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is PHP's mail() function attachment support a bit broken on Windows?

I had a ticket logged by a customer today reporting that PHP's mail() function was timing out on him on one of our Windows 2003 Server boxes when trying to send attachments.

Upon investigating I was able to reproduce his problem. Messages containing smallish attachments at around the 30-60Kb size were taking 15-20 seconds to be processed by the mail() function. Larger attachments of around 360-500Kb were taking longer than the maximum script execution time permitted (90 seconds).

I was able to reproduce the problem on two different Windows 2003 servers and a Windows 2008R2 server. I also tried three different versions of PHP (5.2.14, 5.2.17 and 5.3.6 - all 32 bit and all non-threadsafe builds as per Microsoft's recommendations for running PHP on Windows).

In all cases mail was being sent via SMTP (i.e. not using a sendmail implementation). I tried three different SMTP scenarios:

  • Delivery directly to our SMTP smarthost cluster (running exim)
  • Delivery via local IIS SMTP Service which relays to our smarthosts
  • Delivery via local IIS SMTP Service but with MX lookup and direct delivery

Regardless of the above, sending attachments was still suboptimal which means the problem can't be pinned on a slow relay.

I then ran the same code on our CentOS servers which didn't exhibit any of these issues, the mail() function returned almost immediately. However PHP on these servers are configured to use sendmail.

I then decided to spelunk the PHP source code to find out what the implementation of the mail() function looked like and discovered this code in ext/standard/mail.c:

if (!sendmail_path) {
#if (defined PHP_WIN32 || defined NETWARE)
    /* handle old style win smtp sending */
    if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, headers, subject, to, message, NULL, NULL, NULL TSRMLS_CC) == FAILURE) {
        if (tsm_errmsg) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", tsm_errmsg);
            efree(tsm_errmsg);
        } else {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", GetSMErrorText(tsm_err));
    }
        return 0;
    }
    return 1;
#else
    return 0;
#endif

TSendMail() is implemented in another source file (win32/sendmail.c). Ultimately all data sent to a SMTP server seems to be passed synchronously via a function called Post() in sendmail.c which looks like:

static int Post(LPCSTR msg)
{
    int len = strlen(msg);
    int slen;
    int index = 0;

    while (len > 0) {
        if ((slen = send(sc, msg + index, len, 0)) < 1)
            return (FAILED_TO_SEND);
        len -= slen;
        index += slen;
    }
    return (SUCCESS);
}

The send() function is a winsock2 function.

I am wondering if buffer size (8K is the default according to the KB article below) or lack of tuning is having some impact here for larger amounts of data. There are no calls to setsockopt() to specify a buffer size or any other options to optimise calls to send().

Perhaps the mail() function on Windows using SMTP delivery is not intended to be used for sending large emails?

I'd be interested to know if anyone else has looked at this code or experienced the same thing.

Design issues - Sending small data segments over TCP with Winsock

Just to be clear, we already have an alternative solution for the customer (SwiftMailer) in place now so this isn't about getting recommendations for alternatives.

like image 634
Kev Avatar asked Sep 23 '11 01:09

Kev


People also ask

How do you check PHP mail () is working?

to check if it is sending mail as intended; <? php $email = "[email protected]"; $subject = "Email Test"; $message = "this is a mail testing email function on server"; $sendMail = mail($email, $subject, $message); if($sendMail) { echo "Email Sent Successfully"; } else { echo "Mail Failed"; } ?>

Why does PHP mail not work?

It could be, for example, because you're missing necessary parameters, have typos in your recipient's email address, or need to set up an SMTP relay. Out of all the possible options as to why your PHP mail() isn't sending email, we found that most issues stem from Postfix being configured incorrectly.

How to send email using PHP mail() function?

PHP makes use of mail() function to send an email. This function requires three mandatory arguments that specify the recipient's email address, the subject of the the message and the actual message additionally there are other two optional parameters. mail( to, subject, message, headers, parameters );

What type of headers have to be added in the mail function to attach a file?

The PHP mail() function with some MIME type headers can be used to send email with attachment in PHP. In the following example code, MIME and Content-Type headers are used with mail() function to send email with attachment using PHP. $to – Recipient email address.


1 Answers

My thoughts would use http://glob.com.au/sendmail/ on Windows with the PEAR Mail class: http://pear.php.net/package/Mail. This could possibly be a workaround for the latency you're experiencing. I'm thinking this won't be able to bypass the buffer, but I think it would be worth a shot.

Also, I have never used it, but I've heard good things about SwiftMailer: http://swiftmailer.org/

like image 101
David Weitz Avatar answered Sep 29 '22 21:09

David Weitz