note 75460 deleted from function.mail by crell
| From: | crell@php.net | Date: | Tue, 12 Apr 2022 01:10:20 +0000 |
| Subject: | note 75460 deleted from function.mail by crell | ||
| References: | 1 | Groups: | php.notes |
| Request: | Send a blank email to php-notes+get-218233@lists.php.net to get a copy of this message | ||
Note Submitter: Alex Jaspersen
----
For qmail users, I have written a function that talks directly to qmail-queue, rather than going
through the sendmail wrapper used by mail(). Thus it allows more direct control over the message
(for example, you can adapt the function to display "undisclosed recipients" in to the To:
header). It also performs careful validation of the e-mail addresses passed to it, making it more
difficult for spammers to exploit your scripts.
Please note that this function differs from the mail() function in that the from address must be
passed as a _separate_ argument. It is automatically put into the message headers and _does not_
need to be included in $additional_headers.
$to can either be an array or a single address contained in a string.
$message should not contain any carriage return characters - only linefeeds.
No validation is performed on $additional_headers. This is mostly unnecessary because qmail will
ignore any additional To: headers injected by a malicious user. However if you have some strange
mail setup it might be a problem.
The function returns false if the message fails validation or is rejected by qmail-queue, and
returns true on success.
<?php
function qmail_queue($to, $from, $subject, $message, $additional_headers = "")
{
// qmail-queue location and hostname used for Message-Id
$cmd = "/var/qmail/bin/qmail-queue";
$hostname = trim(file_get_contents("/var/qmail/control/me"));
// convert $to into an array
if(is_scalar($to))
$to = array($to);
// BEGIN VALIDATION
// e-mail address validation
$e = "/^[-+\\.0-9=a-z_]+@([-0-9a-z]+\\.)+([0-9a-z]){2,4}$/i";
// from address
if(!preg_match($e, $from)) return false;
// to address(es)
foreach($to as $rcpt)
{
if(!preg_match($e, $rcpt)) return false;
}
// subject validation (only printable 7-bit ascii characters allowed)
// needs to be adapted to allow for foreign languages with 8-bit characters
if(!preg_match("/^[\\040-\\176]+$/", $subject)) return false;
// END VALIDATION
// open qmail-queue process
$dspec = array
(
array("pipe", "r"), // message descriptor
array("pipe", "r") // envelope descriptor
);
$pipes = array();
$proc = proc_open($cmd, $dspec, $pipes);
if(!is_resource($proc)) return false;
// write additional headers
if(!empty($additional_headers))
{
fwrite($pipes[0], $additional_headers . "\n");
}
// write to/from/subject/date/message-ID headers
fwrite($pipes[0], "To: " . $to[0]); // first recipient
for($i = 1; $i < sizeof($to); $i++) // additional recipients
{
fwrite($pipes[0], ", " . $to[$i]);
}
fwrite($pipes[0], "\nSubject: " . $subject . "\n");
fwrite($pipes[0], "From: " . $from . "\n");
fwrite($pipes[0], "Message-Id: <" . md5(uniqid(microtime())) . "@" .
$hostname . ">\n");
fwrite($pipes[0], "Date: " . date("r") . "\n\n");
fwrite($pipes[0], $message);
fwrite($pipes[0], "\n");
fclose($pipes[0]);
// write from address and recipients
fwrite($pipes[1], "F" . $from . "\0");
foreach($to as $rcpt)
{
fwrite($pipes[1], "T" . $rcpt . "\0");
}
fwrite($pipes[1], "\0");
fclose($pipes[1]);
// return true on success.
return proc_close($proc) == 0;
}
?>