Andrew Ceban
Andrew Ceban

Reputation: 118

Send PHP Mail in intervals for Joomla com_mailto

I have following script which as I understand sends e-mails unlimited, but I would like to limit it to send 400 e-mails then wait 30 minutes , is there such a possibility? This is restriction from my host.

 <?php
    /**
     * @version     $Id: controller.php 21078 2011-04-04 20:52:23Z dextercowley $
     * @package     Joomla
     * @subpackage  MailTo
     * @copyright   Copyright (C) 2005 - 2010 Open Source Matters. All rights reserved.
     * @license     GNU/GPL, see LICENSE.php
     * Joomla! is free software. This version may have been modified pursuant to the
     * GNU General Public License, and as distributed it includes or is derivative
     * of works licensed under the GNU General Public License or other free or open
     * source software licenses. See COPYRIGHT.php for copyright notices and
     * details.
     */

    // Check to ensure this file is included in Joomla!
    defined('_JEXEC') or die( 'Restricted access' );

    jimport('joomla.application.component.controller');

    define('MAILTO_TIMEOUT', 20);

    /**
     * @package     Joomla
     * @subpackage  MailTo
     */
    class MailtoController extends JController
    {

        /**
         * Show the form so that the user can send the link to someone
         *
         * @access public
         * @since 1.5
         */
        function mailto()
        {
            $session =& JFactory::getSession();
            $session->set('com_mailto.formtime', time());
            JRequest::setVar( 'view', 'mailto' );
            $this->display();
        }

        /**
         * Send the message and display a notice
         *
         * @access public
         * @since 1.5
         */
        function send()
        {
            global $mainframe;

            // Check for request forgeries
            JRequest::checkToken() or jexit( 'Invalid Token' );
            $session =& JFactory::getSession();
            $db =& JFactory::getDBO();

            // we return time() instead of 0 (as it previously was), so that the session variable has to be set in order to send the mail
            $timeout = $session->get('com_mailto.formtime', time());
    //      if($timeout == 0 || time() - $timeout < MAILTO_TIMEOUT) {
    //          JError::raiseNotice( 500, JText:: _ ('EMAIL_NOT_SENT' ));
    //          return $this->mailto();
    //      }
            // here we unset the counter right away so that you have to wait again, and you have to visit mailto() first
            $session->set('com_mailto.formtime', null);

            jimport( 'joomla.mail.helper' );

            $SiteName   = $mainframe->getCfg('sitename');
            $MailFrom   = $mainframe->getCfg('mailfrom');
            $FromName   = $mainframe->getCfg('fromname');

                    $link           = MailtoHelper::validateHash(JRequest::getString('link', '', 'post'));

            // Verify that this is a local link
    //      if((!$link) || (!JURI::isInternal($link))) {
    //          //Non-local url...  
    //          JError::raiseNotice( 500, JText:: _ ('EMAIL_NOT_SENT' ));
    //          return $this->mailto();
    //      }

            // An array of e-mail headers we do not want to allow as input
            $headers = array (  'Content-Type:',
                                'MIME-Version:',
                                'Content-Transfer-Encoding:',
                                'bcc:',
                                'cc:');

            // An array of the input fields to scan for injected headers
            $fields = array ('mailto',
                             'sender',
                             'from',
                             'subject',
                             );

            /*
             * Here is the meat and potatoes of the header injection test.  We
             * iterate over the array of form input and check for header strings.
             * If we find one, send an unauthorized header and die.
             */

            foreach ($fields as $field)
            {
                foreach ($headers as $header)
                {
                    if (strpos($_POST[$field], $header) !== false)
                    {
                        JError::raiseError(403, '');
                    }
                }
            }

            /*
             * Free up memory
             */
            unset ($headers, $fields);

            $email              = JRequest::getString('mailto', '', 'post');
            $sender             = JRequest::getString('sender', '', 'post');
            $from               = JRequest::getString('from', '', 'post');
            $body               = JRequest::getString('body', '', 'post');
            $vacancy            = JRequest::getString('vacancy', '', 'post');

            $subject            = 'A message of interest sent by '.$email;
    //      $subject            = JRequest::getString('subject', $subject_default, 'post');

            // Check for a valid to address
            $error  = false;
            if ( ! $email  || ! JMailHelper::isEmailAddress($email) )
            {
                $error  = JText::sprintf('EMAIL_INVALID', $email);
                JError::raiseWarning(0, $error );
            }

            // Check for a valid from address
            if ( ! $from || ! JMailHelper::isEmailAddress($from) )
            {
                $error  = JText::sprintf('EMAIL_INVALID', $from);
                JError::raiseWarning(0, $error );
            }

            if ( $error )
            {
                return $this->mailto();
            }

            // Build the message to send
            if($vacancy) {
                $query = 'SELECT DISTINCT i.*, (i.plus / (i.plus + i.minus) ) * 100 AS votes, cy.title AS country, emp.comp_name AS employer,' .
                ' CASE WHEN CHAR_LENGTH(i.alias) THEN CONCAT_WS(\':\', i.id, i.alias) ELSE i.id END as slug' .
                ' FROM jos_career_items AS i' .
                ' LEFT JOIN jos_career_cats_item_relations AS rel ON rel.itemid = i.id' .
                ' LEFT JOIN jos_career_country AS cy ON cy.id = i.id_country' .
                ' LEFT JOIN jos_career_employer AS emp ON emp.id = i.employer_id' .
                ' WHERE i.id = ' . $vacancy;
                $db->setQuery($query);
                $itemData = $db->loadObjectList();
                $vacancyData = $itemData[0];
                $body   = '<div style="margin: 10px auto; width:100%;font-size:13px;">
                        <div><img src="http://search4staff.com/newsite/images/logo.png" alt=""/></div>
                        <div style="padding: 10px;">'.$sender.' saw the job below advertised on www.search4staff.com and thought it might be of interest to you</div>
                        <div style="clear:both; border-bottom: 1px dotted #cccccc;"></div>
                        <div style="font-size:16px;font-weight:bold;padding: 5px;">'.$vacancyData->title.'</div>
                        <div>'.$vacancyData->text1.'</div>
                        <div>'.$vacancyData->text2.'</div>
                        <div style="clear:both; border-bottom: 1px dotted #cccccc;"></div>
                        <div style="padding:10px;">Click here to view the job ad: '.$link.'</div>
                        <div style="clear:both; border-bottom: 1px dotted #cccccc;"></div>
                        <div style="padding:10px;">'.$sender.' message: '.$body.'</div>
                        <div style="clear:both; border-bottom: 1px dotted #cccccc;"></div>
                        <div style="padding:10px;">HR-Consulting, Professional Search Consultants<br/>www.search4staff.com<br/>phone: +373 22 20 91 33<br/>
                      </div>
                      ';
            } else {
                $msg    = JText :: _('EMAIL_MSG');
                $body   = sprintf( $msg, $SiteName, $sender, $from, $link);    
            }

            // Clean the email data
            $subject = JMailHelper::cleanSubject($subject);
            $body    = JMailHelper::cleanBody($body);
            $sender  = JMailHelper::cleanAddress($sender);

            // Send the email
            if ( JUtility::sendMail($from, $sender, $email, $subject, $body, true) !== true )
            {
                JError::raiseNotice( 500, JText:: _ ('EMAIL_NOT_SENT' ));
                return $this->mailto();
            }

            JRequest::setVar( 'view', 'sent' );
            $this->display();
        }


        function upload_cv() {
            global $mainframe;

            $postData = JRequest::get('post');

            if(file_exists(JPATH_BASE."/CV"))
            {
                if(!file_exists(JPATH_BASE."/CV/".$postData['item_id'])) {
                    mkdir(JPATH_BASE."/CV/".$postData['item_id']);
                }
                chmod(JPATH_BASE."/CV/".$postData['item_id'], 0777);
            }

            $file =  JPATH_BASE."/CV/".$postData['item_id'].'/'.$postData['item_code'].'_'.basename($_FILES['uploadfile']['name']);

            move_uploaded_file($_FILES['uploadfile']['tmp_name'], $file);

            $mailfrom       = $mainframe->getCfg( 'mailfrom' );
            $fromname       = $mainframe->getCfg( 'fromname' );

            $subject = $postData['item_code'].' UPLOAD CV - '.$_FILES['uploadfile']['name'];
            $message = $postData['body'];
            $message .= '<br /><br />Please check attachment!';
            $attachment = $file;

            JUtility::sendMail($mailfrom, 'New CV for '.$postData['item_code'].' from '.$postData['from'], $postData['item_mail'], $subject , $message, true, null, null, $attachment);//$postData['item_mail']
        JRequest::setVar( 'view', 'cvsend' );
        $this->display();
        }
    }

someone sugested

               // Send the email
  if ( JUtility::sendMail($from, $sender, $email, $subject, $body) !== true )
  {
                                      sleep(108000); //30 minutes
                          if ( JUtility::sendMail($from, $sender, $email, $subject, $body) !== true )
                         {
                   JError::raiseNotice( 500, JText:: _ ('EMAIL_NOT_SENT' ));
                   return $this->mailto();
                                                }
  }

If it fails it will wait and try again. Not sure if you get a timeout somewhere else like max execution time.

Should I e set bigegr value to define('MAILTO_TIMEOUT', 20); ?

Upvotes: 0

Views: 839

Answers (3)

Chad Windnagle
Chad Windnagle

Reputation: 655

The best thing to do if you wanted to do scheduled emails would be to use a cron job on the server. That is specifically designed to allow you to set up a schedule for interval script execution. Building the entire thing out would take some doing but what you'd end up with is a database table which has all your emails by id, then a email-id-to-address-id table so you can track your email user ids and the email ids that have been sent.

Each time the script executes it checks for the latest message id, then begins queuing and sending the message to everyone on the list, recording their ids in your 'sent' table. The next time it executes it checks both the sent table and the address book table and finds which addresses haven't received it, and queues them up for sending (and then adding them to the sent table).

That said I agree with Elin that this method is likely going to cause you trouble, and hosts certainly do not like it. Your best bet would be to use the MailChimp API or generate messages with AcyMailing. AcyMailing is surprisingly powerful building a plugin for it to generate messages is equally simple and would probably be less code than rolling out the script above. You can easily have different newsletters for different groups (job posting categories), and it already does most of the sent / unsent logic for you.

Upvotes: 0

Elin
Elin

Reputation: 6770

Really, use an extensions that understands what the laws about spamming are and also about how to work with throttling such as your host requires. The Joomla mail_to is designed for registration confirmation and password resets, it is not a full featured mail management class. This is a place where you can be shut down by a host or make a mistake (like forgetting to include a link to a physical address or an incorrect unsubscribe link) that gets you big fines and your email addresses blacklisted by hosts. Even CiviCRM is moving to MailChimp integration and they have one of the most sophisticated mailing list management systems you can find.

Upvotes: 1

Riccardo Zorn
Riccardo Zorn

Reputation: 5615

Since the script appears in a controller I am assuming multiple instances of the script may be invoked at once. This makes all uses of timeouts moot.

i.e. if you divide 30 minutes / 400 = 4.5 seconds, and wait 4.5 seconds before sending the mail, you'd think you're honouring the limit. But you're not! If 500 users click at the same time, you'll have them all waiting 4.5 seconds, then send 500 emails at the same time.

You need to set up

  • a list, possibly stored in the database, with the details of the emails you wish to send. Your code above would limit itself to populating the list.
  • a scheduled task that iterates over the list and sends email, adding a timestamp to the list items above once the mail is sent. A quick query for all mails in the past 30 minutes will show if it has sent too many.

Upvotes: 1

Related Questions