I have a simplistic webform running on bluehost that when submitted, sends out mail to a distribution list.
Recently the mail server went down, and my php mail script hung when the submit button was clicked. The user tried one more time, and again the page just hung. Later when the mail server came back up, it sent out multiple copies of the mail.
My question is, is there an easy way to put some sort of timeout on the script, so that if either a set period of time passes and the mail server has not acknowledged the request, or alternatively, if the number of unsuccessful attempts has exceeded a preset number, then the script will stop attempting to send the mail?
My script:
// try to send email
if (mail($to,$subject,$msg,$headers)) {
header('Location: complete.php');
} else
{
header('Location: incomplete.php');
}
Thanks for any advice you may be able to give,
Rich.
PS. I do not have access to change any settings on the server, although I do have a .htaccess file saved to the local directory.
This is not possible. You may want to have a look at phpmailer which is way more advanced than the built-in mail() function php provides.
mail() always returns true if the mail has been submitted to the mailserver (and actually should not hang when the mailserver is down, but return false).
Related
We have shared hosting servers which use PHP fastcgi (on IIS) for several clients (shared hosting). Regularly clients use old exploitable code which causes holes in their applications that eventually gets used by hackers to install malicious code. Most of the time this code is being used to send spam from our servers.
We have no control over our clients code, so patching the holes is quite impossible.
We would however like to block the clients sending spam once they send more then X email messages in Y amount of time.
The setup is fastcgi based, so there is little relation between php and the webserver.
PHP sends its mail through SMTP on localhost. The mailserver allows relay of all localhost connections (obviously).
One thing that goes through my mind is setting an environment variable containing an identifier in the fastcgi environment and using php's prepend file option to add a header to all mail send by php's mailer. After that we could use that mail header to identify the spamming culprit.
The option above still would not take care of spam scripts using regular telnet (telnet localhost, HELO, MAIL FROM etc... ) when sending email.
My question to you: is the idea that i've mentioned the best and perhaps only option to deal with our issue? Or are there better solutions for this situation? And if so, please explain how you would deal with the issue.
You can filter that on you MTA (message transfer agent). For example, allow no more than 50 emails in 1 hour for each user in Exim ( http://www.exim.org ) config file (/etc/exim/exim.conf):
begin acl
acl_check_not_smtp:
warn ratelimit = 0 / 1h / strict / $sender_address_local_part
log_message = Sender rate $sender_rate / $sender_rate_perio
acl_not_smtp = acl_not_smtp
begin acl
acl_not_smtp:
deny message = Sender rate overlimit - $sender_rate / $sender_rate_period
ratelimit = 50 / 1h / strict
accept
And no matter how they try to send, via php mail() or other method.
Most shared hosts block the use of PHP's mail() function, as that can be easily exploited. Instead they advice using sendmail or similar scripts which require SMTP authentication before sending. Assuming you're not already doing this, once implemented, you should be able to keep track of number of emails sent from a particular domain/email account and put restrictions on it.
Okay, stick with me on this one. I have not implemented it but it looks good.
The concept here is that you could
run a php file before EVERY page on your customers site
in that php file rename the mail function to mail_internal().
in that php create a new function called mail to do your check / verification that your customer is allowed to send mail, and if they are call the mail_internal() function with the same parameters.
You will have to install the runkit PECL extension
http://us.php.net/manual/en/runkit.installation.php
Changes
in php.ini
auto_prepend_file /var/www/allclients_forcedfile.php
in /var/www/allclients_forcedfile.php
runkit_function_rename ( "mail" , "mail_internal" );
function mail ( $to , $subject , $message, $additional_headers = "", $additional_parameters ="" )
{
$args = func_get_args();
error_log("mail_internal : $_SERVER[HTTP_HOST] : ".implode(" : ",$args));
//lookup whether you want to send more mail for this client maybe by keeping a counter in some file in the $SERVER[DOCUMENT_ROOT]
if($sendmoremail)
return mail_internal ( $args[0], $args[1] , $args[2], $args[3] , $args[4] );
return false;
}
As expected it seems Stack Overflow is not the right place for this question. The provided answers do not expose some clear method to identify FastCGI sessions' connections to the MTA (SMTP) server.
I will go with my initial concept of adding an identifier to php's environment. This identifier can be read in PHP's prepend file by using the getenv() function. This identifier can then be added to mail headers for outgoing mail.
Furthermore I have enabled the mail.add_x_header ini setting which will help identify which script caused the spam run.
I am leaving the question open for the bounty duration, hoping other options will magically appear :)
Currently I have a mail server configured (a real one from my ISP) and mail internal and external works on the command line. In PHP only external users work.
For testing I would like to send to internal users only.
( Ideally I would like to set up lots of aliases that point to one user so mail to:
tom#localhost.com, dick#localhost.com, harry#localhost.com end up in /var/mail/johnsmith )
I'd be greatful if someone could help here. I'm hesitant to edit the postfix config files...
On the command line johnsmith#localhost works but not in PHP. It's using cakePHP and I checked the value of $email-addr just before the send ($this->Email->send();) and the value is johnsmith#localhost. I'm not that familiar with cakePHP yet. The var/log/mail shows nothing, only external email addresses.
(server is Suse linux)
You can use the basic mail php function
http://php.net/manual/en/function.mail.php
Under linux, mail php function relies on sendmail, just check that sendmail is properly installed.
In /etc/postfix/main.cf add localhost.com:
mydestination = $myhostname, localhost.$mydomain,localhost,localhost.com
This allows sending on "localuser#localhost.com" via command line. I loaded up a test php mail form script in a browser and it works, sending mail through to /var/mail/localuser.
At the moment it means I have to check each local users /var/mail file. I'm working on the alias. My first attempt at that failed.
I'm working on a php project where all emails are sent via the mail() function.
That's quite a problem to prepare and test those emails because mail() function fails on the localhost and I should constantly rewrite my code to print out the email before sending, check it and asume that on server it would be sent ok.
Is there a way to somehow manage such situations?
I will be very happy if there is a way to save the messages on hard disk or send them only to the one specific email address, not to the real recipients, without or with slight modification of the code. Some useful software or advices are so appreciated.
Thanks!
P.S. On the localhost I'm using WAMP package as a webserver.
http://www.toolheap.com/test-mail-server-tool/
Test mail server tool for Windows is awesome! Every time you send an email on local host, it just pops that email up in your favorite email reader (I.e. outlook, postbox etc)
I use it exclusively to test all my web apps on WAMP!
No server changes needed - just download and install - then send an email and see it in action.
Oh - and it's free!
Here is the solution I found to get around this problem.
1. Create the sendmail.php file somewhere in the wamp dir, e.g. d:\wamp\apps\sendmail.php. Here is it's source:
/* Path where emails will be stored */
define('DST', 'd:/wamp/tmp/sendmail/');
/* Extract the message from the stdin */
$message = '';
while(($line = fgets(STDIN)) !== false) {
$message .= $line;
}
/* Save message to file */
file_put_contents(DST.date('Y-m-d_H-i-s_').md5($message).'.eml', $message);
2. Uncomment and edit the sendmail_path parameter in the php.ini to this:
sendmail_path = "D:\wamp\bin\php\php5.3.5\php.exe D:\wamp\apps\sendmail.php"
All messages that are sent via the mail() function will be captured and stored in the specified directory.
Install fakemail. It acts as a local SMTP server and saves all mails in a folder.
You could wrap the mail() function, like:
function my_send_mail(/*...*/) {
if (is_localhost()) {
// just save the mail to text.
} else {
// call mail() and send mail
}
}
And use this function instead of use mail() directly.
I am using php5.
Are there some settings or a simple php.ini directive that would redirect all the emails to a folder?
I want on the development machine to have all the emails generated by the system not sent to the actual receiver but put in a folder.
Thanks.
I used to have some code like this (kinda pseudocode):
define ('DEBUG', true);
function send_email($to, $subject, $body) {
if (DEBUG) {
file_put_contents('some_folder/' . $to . date('dmY-His') . '.html', $body);
}else{
// Actual code to send email
}
}
But i agree with others, it's easier/better to setup an development email account to receive those emails.
I don't think you will be able to do something like this. Mails are sent by a mail server so it must be your mail server that writes them to a file instead of sending them.
Why not simply send it to a special development email?
Sample:
define('DEBUG', true);
if(DEBUG)
{
// Override recipient
$recipient = 'development#domain.tld';
}
// Send mail...
No settings that I'm aware of in PHP itself. However, if you're using Postfix on your development server, here's a recipe I cooked up to redirect all outbound email to a single (local) address:
/etc/postfix/main.cf: (add this to the existing file, don't replace everything)
virtual_alias_maps = regexp:/etc/postfix/virtual
/etc/postfix/virtual:
/.*/ duskwuff#localhost
You can configure your mail server to accept SMTP messages as normal, but make it unable to forward them onto another mail server. If your mail server supports it, make it redirect all messages to a postmaster account, or any other address of your choice.
This means that PHP will behave as normal, everything will appear to work straight away with the message, but it just won't go to the 'intended' recipient.
It also means that you can inspect headers (pretty much as they would normally appear), to support debugging.
There are many ways to do this. Basically, you need to define the sendmail command in your php.ini to point to a program or script which will save the mail locally.
One solution is this:
Catch emails with php script
Another is this:
Mail catcher
I am using postfix for my linux mail server. The goal is to have any incoming mail dumped into a database with the headers and message information, then the e-mail being deleted from the mail server. Is there any way to make postfix post a message to a php file everytime a new e-mail comes in then delete the e-mail message? The only other way I can see to make a script to poll the e-mail server, read each mail and transfer the contents to a database, then delete the messages from the mail server. Being able to have postfix automatically execute the php script for all new incoming mails would be a better choice. If it makes a difference, the mail server and the server with the php file is the same. Any direction in this matter would be greatly appreciated.
use .forward, /etc/aliases, hashtable etc to forward mail to a script.
In /etc/aliases, I have
mysite-confirm: |/home/mysite/confirm.sh
In confirm.sh, I have
#!/bin/sh
basedir=/home/mysite/www
php -d include_path=$basedir/includes -f $basedir/cli/confirm.php
In confirm.php, the magic happens:
$contents = file_get_contents("php://stdin");
do_magic_with_mail($contents);
All quite simple and rigid. The only downside is that you could mail mysite-confirm#any_domain_I_host.com, but you can fix that with the right aliases / virtualmaps etc.