Example #1
0
 /**
  * Disguise action
  *
  * Administrator is able to disguise (and login) as other users.
  * This method will facilitate this functionalities.
  *
  * TODO better PHPDOC
  */
 public function page_disguise()
 {
     // newUserID variable need to be passed in as a POST variable
     $reqUserID = IEM::requestGetPOST('newUserID', 0, 'intval');
     if (empty($reqUserID)) {
         IEM::redirectTo('index');
         return false;
     }
     // Attempt to login user with different ID
     if (!IEM::userLogin($reqUserID, false)) {
         IEM::redirectTo('index');
         return false;
     }
     IEM::redirectTo('index');
     return true;
 }
	/**
	* ProcessJob
	* Processes an autoresponder queue
	* Checks a queue for duplicates, makes sure the queue is present and has recipients in it and then calls ActionJob to handle the rest
	*
	* @param Int $queueid Autoresponder queue to process. This is passed to ActionJob
	*
	* @see GetUser
	* @see RemoveDuplicatesInQueue
	* @see QueueSize
	* @see ActionJob
	* @see UnprocessQueue
	*
	* @return True Always returns true
	*/
	function ProcessJob($queueid=0)
	{
		$queueid = (int)$queueid;

		$this->user = GetUser($this->jobowner);
		IEM::userLogin($this->jobowner, false);

		$queuesize = $this->QueueSize($queueid, 'autoresponder');

		if ($this->Debug) {
			error_log(time() . "\t" . __FILE__ . "\t" . __LINE__ . "\t" . "queuesize: " . $queuesize . " for queueid " . $queueid . "\n", 3, $this->LogFile);
		}

		$jobid_query = "SELECT jobid FROM " . SENDSTUDIO_TABLEPREFIX . "jobs WHERE queueid='" . $queueid . "'";
		$jobid_result = $this->Db->Query($jobid_query);
		$jobid = $this->Db->FetchOne($jobid_result, 'jobid');

		if (!$jobid) {
			if ($this->Debug) {
				error_log(time() . "\t" . __FILE__ . "\t" . __LINE__ . "\t" . "no jobid (result " . gettype($jobid_result) . "; " . $jobid_result . ")" . "\n", 3, $this->LogFile);
				error_log(time() . "\t" . __FILE__ . "\t" . __LINE__ . "\t" . "Returning" . "\n", 3, $this->LogFile);
			}
			IEM::userLogout();
			return true;
		}

		$timenow = $this->GetServerTime();
		$query = "UPDATE " . SENDSTUDIO_TABLEPREFIX . "jobs SET lastupdatetime=" . $timenow . " WHERE jobid='" . $jobid . "'";
		$update_job_result = $this->Db->Query($query);

		if ($queuesize <= 0) {
			if ($this->Debug) {
				error_log(time() . "\t" . __FILE__ . "\t" . __LINE__ . "\t" . "Deleting job " . $jobid . " and then returning" . "\n", 3, $this->LogFile);
			}
			$this->Db->Query("DELETE FROM " . SENDSTUDIO_TABLEPREFIX . "jobs WHERE jobid='" . $jobid . "'");
			IEM::userLogout();
			return true;
		}

		if ($this->Debug) {
			error_log(time() . "\t" . __FILE__ . "\t" . __LINE__ . "\t" . "Actioning jobid " . $jobid . "\n", 3, $this->LogFile);
		}

		$finished = $this->ActionJob($queueid, $jobid);

		if ($this->Debug) {
			error_log(time() . "\t" . __FILE__ . "\t" . __LINE__ . "\t" . "Finished: " . $finished . "\n", 3, $this->LogFile);
		}

		// we need to turn 'processed' emails back to normal so we can check them next time.
		$this->UnprocessQueue($queueid);

		if ($this->Debug) {
			error_log(time() . "\t" . __FILE__ . "\t" . __LINE__ . "\t" . "Deleting jobid " . $jobid . "\n", 3, $this->LogFile);
		}

		$this->Db->Query('DELETE FROM ' . SENDSTUDIO_TABLEPREFIX . 'jobs_lists WHERE jobid=' . intval($jobid));
		$this->Db->Query("DELETE FROM " . SENDSTUDIO_TABLEPREFIX . "jobs WHERE jobid=" . intval($jobid));

		IEM::userLogout();
		return true;
	}
Example #3
0
         if (!is_array($tempCookie) || !isset($tempCookie['user'])) {
             break;
         }
         // Get user
         $tempUser = new User_API();
         $tempUser->Load(intval($tempCookie['user']));
         // Check if the user is a valid user
         if (!isset($tempUser->settings['LoginCheck']) || !$tempUser->userid || !$tempUser->Status()) {
             break;
         }
         // Check whether or not the random number matches
         if (!$tempUser->settings['LoginCheck'] == $tempCookie['rand']) {
             break;
         }
         // The cookie is valid! Update session accordingly
         IEM::userLogin($tempUser->userid);
         $tempValid = true;
         // Check if we have login preferences
         $tempLoginPref = IEM::requestGetCookie('IEM_LoginPreference', array());
         if (is_array($tempLoginPref) && isset($tempLoginPref['takemeto'])) {
             header('Location: ' . SENDSTUDIO_APPLICATION_URL . '/admin/' . $tempLoginPref['takemeto']);
         }
         break;
     }
     if (!$tempValid) {
         $page = 'login';
     }
     unset($tempValid);
     unset($tempCookie);
     unset($tempUser);
 } else {
Example #4
0
	/**
	* Process
	* All the action happens here.
	* If you are not logged in, it will print the login form.
	* Submitting that form will then try to authenticate you.
	* If you are successfully authenticated, you get redirected back to the main index page (quickstats etc).
	* Otherwise, will show an error message and the login form again.
	*
	* @see ShowLoginForm
	* @uses AuthenticationSystem::Authenticate()
	*
	* @return Void Doesn't return anything. Checks the action and passes it off to the appropriate area.
	*/
	function Process()
	{
		$action = IEM::requestGetGET('Action', '', 'strtolower');
		switch ($action) {
			case 'forgotpass':
				$this->ShowForgotForm();
			break;

			case 'changepassword':
				if (!IEM::sessionGet('ForgotUser')) {
					$this->ShowForgotForm('login_error', GetLang('BadLogin_Link'));
					break;
				}

				$userapi = GetUser(-1);
				$loaded = $userapi->Load(IEM::sessionGet('ForgotUser'));

				if (!$loaded) {
					$this->ShowForgotForm('login_error', GetLang('BadLogin_Link'));
					break;
				}

				$password = IEM::requestGetPOST('ss_password', false);
				$confirm = IEM::requestGetPOST('ss_password_confirm', false);

				if ($password == false || ($password != $confirm)) {
					$this->ShowForgotForm_Step2($userapi->Get('username'), 'login_error', GetLang('PasswordsDontMatch'));
					break;
				}

				$userapi->password = $password;
				$userapi->Save();

				$code = md5(uniqid(rand(), true));

				$userapi->ResetForgotCode($code);

				$this->ShowLoginForm('login_success', GetLang('PasswordUpdated'));
			break;

			case 'sendpass':
				$user = GetUser(-1);
				$username = IEM::requestGetPOST('ss_username', '');

				/**
				 * Fix vulnerabilities with MySQL
				 * Documented here: http://www.suspekt.org/2008/08/18/mysql-and-sql-column-truncation-vulnerabilities/
				 *
				 * Basically MySQL is truncating values in a column
				 */
					$username = preg_replace('/\s+/', ' ', $username);
					$username = trim($username);
				/**
				 * -----
				 */

				$founduser = $user->Find($username);
				if (!$founduser) {
					$this->ShowForgotForm('login_error', GetLang('BadLogin_Forgot'));
					break;
				}

				$user->Load($founduser, false);

				$code = md5(uniqid(rand(), true));

				$user->ResetForgotCode($code);

				$link = SENDSTUDIO_APPLICATION_URL . '/admin/index.php?Page=Login&Action=ConfirmCode&user='******'&code=' . $code;

				$message = sprintf(GetLang('ChangePasswordEmail'), $link);

				$email_api = $this->GetApi('Email');
				$email_api->Set('CharSet', SENDSTUDIO_CHARSET);
				$email_api->Set('Multipart', false);
				$email_api->AddBody('text', $message);
				$email_api->Set('Subject', GetLang('ChangePasswordSubject'));

				$email_api->Set('FromAddress', SENDSTUDIO_EMAIL_ADDRESS);
				$email_api->Set('ReplyTo', SENDSTUDIO_EMAIL_ADDRESS);
				$email_api->Set('BounceAddress', SENDSTUDIO_EMAIL_ADDRESS);

				$email_api->SetSmtp(SENDSTUDIO_SMTP_SERVER, SENDSTUDIO_SMTP_USERNAME, @base64_decode(SENDSTUDIO_SMTP_PASSWORD), SENDSTUDIO_SMTP_PORT);

				$user_fullname = $user->Get('fullname');

				$email_api->AddRecipient($user->emailaddress, $user_fullname, 't');

				$email_api->Send();

				$this->ShowForgotForm_Step2($username,'login_success', sprintf(GetLang('ChangePassword_Emailed'), $user->emailaddress));
			break;

			case 'confirmcode':
				$user = IEM::requestGetGET('user', false, 'intval');
				$code = IEM::requestGetGET('code', false, 'trim');

				if (empty($user) || empty($code)) {
					$this->ShowForgotForm('login_error', GetLang('BadLogin_Link'));
					break;
				}

				$userapi = GetUser(-1);
				$loaded = $userapi->Load($user, false);

				if (!$loaded || $userapi->Get('forgotpasscode') != $code) {
					$this->ShowForgotForm('login_error', GetLang('BadLogin_Link'));
					break;
				}

				IEM::sessionSet('ForgotUser', $user);

				$this->ShowForgotForm_Step2($userapi->Get('username'));
			break;

			case 'login':
				$auth_system = new AuthenticationSystem();
				$username = IEM::requestGetPOST('ss_username', '');
				$password = IEM::requestGetPOST('ss_password', '');
				$result = $auth_system->Authenticate($username, $password);
				if ($result === -1) {
					$this->ShowLoginForm('login_error', GetLang('PleaseWaitAWhile'));
					break;
				} elseif ($result === -2) {
					$this->ShowLoginForm('login_error', GetLang('FreeTrial_Expiry_Login'));
					break;
				} elseif (!$result) {
					$this->ShowLoginForm('login_error', GetLang('BadLogin'));
					break;
				} elseif ($result && defined('IEM_SYSTEM_ACTIVE') && !IEM_SYSTEM_ACTIVE) {
					$msg = (isset($result['admintype']) && $result['admintype'] == 'a') ? 'ApplicationInactive_Admin' : 'ApplicationInactive_Regular';
					$this->ShowLoginForm('login_error', GetLang($msg));
					break;
				}

                $user = false;
                $rand_check = false;

				IEM::userLogin($result['userid']);

				$oneyear = 365 * 24 * 3600; // one year's time.

				$redirect = $this->_validateTakeMeToRedirect(IEM::requestGetPOST('ss_takemeto', 'index.php'));

				header('Location: ' . SENDSTUDIO_APPLICATION_URL . '/admin/' . $redirect);
				exit();
			break;

			default:
				$msg = false; $template = false;
				if ($action == 'logout') {
					$this->LoadLanguageFile('Logout');
				}
				$this->ShowLoginForm($template, $msg);
			break;
		}
	}
Example #5
0
		/**
		 * eventLinkClicked
		 * This is a listener for "IEM_STATSAPI_RECORDLINKCLICK" event.
		 * It allows trigger email to capture "Newsletter Opened" and "Link Clicked"
		 *
		 * @param EventData_IEM_STATSAPI_RECORDLINKCLICK $eventData Event data object
		 * @return Void Returns nothing
		 *
		 * @uses EventData_IEM_STATSAPI_RECORDLINKCLICK
		 */
		static public function eventLinkClicked(EventData_IEM_STATSAPI_RECORDLINKCLICK $eventData)
		{
			// Only intrested if it is a newsletter
			if ($eventData->statstype != 'newsletter') {
				return;
			}

			// If this click has been recorded previously, ignore it
			if ($eventData->have_been_recorded) {
				return;
			}

			$api = new TriggerEmails_API();
			$linkid = intval($eventData->click_details['linkid']);
			$statid = intval($eventData->click_details['statid']);
			$triggerrecords = $api->GetRecordByAssociatedLinkIDStatID($linkid, $statid);

			// If no trigger records are found, return from function
			if (!$triggerrecords || !is_array($triggerrecords) || !isset($triggerrecords['triggeremails']) || empty($triggerrecords['triggeremails'])) {
				return;
			}

			$recipients = $api->_getSubscriberIDSFromList($eventData->click_details['subscriberid'], array_keys($triggerrecords['lists']));

			foreach ($triggerrecords['triggeremails'] as $trigger) {
				// If receipients has been added to the send queue or have been sent an email for this particular trigger, do not re-add the subscriber again
				if (!$api->CanAddToQueue($trigger['triggeremailsid'], $eventData->click_details['subscriberid'], $trigger['queueid'])) {
					continue;
				}

				$schedule = time() + ($trigger['triggerhours'] * 3600);

				IEM::userLogin($trigger['ownerid'], false);
				$api->AddToQueue($trigger['queueid'], 'triggeremail', $recipients, $schedule);
				IEM::userLogout();
			}

			return;
		}
    /**
     * ProcessJob
     * Does most of the work setting up the job (creates the queue, removes duplicates and so on) to run. Once the job has been set up ready to run, it 'Actions' the job, then marks it as 'finished'.
     *
     * @param Int $jobid The job to run.
     *
     * @see Email_API
     * @see Subscriber_API
     * @see Lists_API
     * @see newsletter_api
     * @see GetUser
     * @see StartJob
     * @see GetJobQueue
     * @see CreateQueue
     * @see JobQueue
     * @see Subscribers_API::GetSubscribers
     * @see RemoveDuplicatesInQueue
     * @see RemoveBannedEmails
     * @see ActionJob
     * @see FinishJob
     *
     * @return Boolean Returns false if the job can't be started. Otherwise runs the job and returns true.
     */
    function ProcessJob($jobid=0) {
        if (!$this->StartJob($jobid)) {
            $this->PauseJob($jobid);
            $this->jobstatus = 'p';
            trigger_error("Unable to start send job {$jobid}");
            return false;
        }

        $user = GetUser($this->jobowner);
        IEM::userLogin($this->jobowner, false);

        $queueid = false;

        // if there's no queue, start one up.
        if (!$queueid = $this->GetJobQueue($jobid)) {
            $sendqueue = $this->CreateQueue('send');
            $queueok = $this->JobQueue($jobid, $sendqueue);
            $send_criteria = $this->jobdetails['SendCriteria'];

            $original_queuesize = $this->jobdetails['SendSize'];

            $queueinfo = array('queueid' => $sendqueue, 'queuetype' => 'send', 'ownerid' => $this->jobowner);

            if (isset($this->jobdetails['Segments']) && is_array($this->jobdetails['Segments'])) {
                $this->Subscriber_API->GetSubscribersFromSegment($this->jobdetails['Segments'], false, $queueinfo, 'nosort');
            } else {
                $this->Subscriber_API->GetSubscribers($send_criteria, array(), false, $queueinfo, $sendqueue);
            }

            $this->Subscriber_API->RemoveDuplicatesInQueue($sendqueue, 'send', $this->jobdetails['Lists']);

            $this->Subscriber_API->RemoveBannedEmails($this->jobdetails['Lists'], $sendqueue, 'send');

            $this->Subscriber_API->RemoveUnsubscribedEmails($this->jobdetails['Lists'], $sendqueue, 'send');

            $queueid = $sendqueue;

            $newsletterstats = $this->jobdetails;
            $newsletterstats['Job'] = $jobid;
            $newsletterstats['Queue'] = $sendqueue;
            $newsletterstats['SentBy'] = $queueinfo['ownerid'];

            $real_queuesize = $this->Subscriber_API->QueueSize($queueid, 'send');

            $newsletterstats['SendSize'] = $real_queuesize;

            $statid = $this->Stats_API->SaveNewsletterStats($newsletterstats);

            /**
             * Process tracker request where because cron was not enabled, we need to parse the option straight away
             * @todo Result for the call to module_Tracker::ParseOptionsForAllTracker() is not being processed and being ignored
             */
            $tempAPIFile = dirname(__FILE__) . '/module_trackerfactory.php';
            if (is_file($tempAPIFile)) {
                require_once($tempAPIFile);
                $temp = array_merge($this->jobdetails, array('statid' => $statid,
                    'stattype' => 'newsletter',
                    'newsletterid' => $this->jobdetails['Newsletter']));

                $status = module_Tracker::ParseOptionsForAllTracker($temp);
            }
            /**
             * -----
             */
            /**
             * So we can link user stats to send stats, we need to update it.
             */
            $this->Stats_API->UpdateUserStats($queueinfo['ownerid'], $jobid, $statid);

            /**
             * The 'queuesize' in the stats_users table is updated by MarkNewsletterFinished in send.php
             * so we don't need to worry about it while setting up the send.
             * That takes into account whether some recipients were skipped because a html-only email was sent etc.
             */
            /**
             * We re-check the user stats in case a bunch of subscribers have joined, or the user has done something like:
             * - create a list
             * - added a few subscribers
             * - scheduled a send
             * - added more subscribers
             */
            $check_stats = $this->Stats_API->ReCheckUserStats($user, $original_queuesize, $real_queuesize, AdjustTime());

            list($ok_to_send, $not_ok_to_send_reason) = $check_stats;
            if (!$ok_to_send) {
                trigger_error(__CLASS__ . '::' . __METHOD__ . " -- " . GetLang($not_ok_to_send_reason), E_USER_WARNING);
                $this->PauseJob($jobid);
                $this->UnapproveJob($jobid);
                IEM::userLogout();
                return false;
            }

            API_USERS::creditEvaluateWarnings($user->GetNewAPI());
        }

        $this->statid = $this->LoadStats($jobid);
        if (empty($this->statid)) {
            trigger_error(__CLASS__ . '::' . __METHOD__ . " -- Cannot find statid. Previous preliminary job process did not get finalized (either CRON died, or it hasn't finished processing the job). Ignoring this job: jobid {$jobid}.", E_USER_NOTICE);
            IEM::userLogout();
            return false;
        }

        $queuesize = $this->Subscriber_API->QueueSize($queueid, 'send');

        // used by send.php::CleanUp
        $this->queuesize = $this->jobdetails['SendSize'];

        /**
         * There's nothing left? Just mark it as done.
         */
        if ($queuesize == 0) {
            $this->jobstatus = 'c';
            $this->FinishJob($jobid);
            IEM::userLogout();
            return true;
        }

        $finished = $this->ActionJob($jobid, $queueid);

        if ($finished) {
            $this->jobstatus = 'c';
            $this->FinishJob($jobid);
        }

        IEM::userLogout();
        return true;
    }
Example #7
0
 /**
  * _ProcessJob
  * This method does the "setup work" for a split test campaign.
  *
  * If a job is passed in that hasn't been started before, it will set everything up:
  * - create a "queue" of recipients
  * - clean the queue (remove banned/duplicate recipients etc)
  * - set up stats for each newsletter in the split test campaign
  * - save stats for the user sending the campaign to take off credits etc
  *
  * If a job is passed in that has been set up before, it just loads the data up.
  *
  * Once it has done either of those, it gives the details to the Splittest_Send_API class
  * and then calls _ActionJob.
  * Based on what that returns, it will either mark the job as complete or not.
  *
  * If the job is a percentage split test send (send to first X % of a list/segment), then
  * it sets the appropriate flags etc to make sure we only send to that first X%.
  * If the job hasn't been set up before, it works out the limit and sets the details in the jobdetails
  * which are then saved in the database in case the job dies or is paused etc.
  *
  * If the job is a percentage split test send and it's time to send to the rest of the list/segment,
  * then it works out the "best" one to send and sets the appropriate variables for that to happen.
  *
  * @param Int $jobid The specific job id we're going to process.
  *
  * @uses _jobid
  * @uses StartJob
  * @uses PauseJob
  * @uses LoadJob
  * @uses GetUser
  * @uses GetJobQueue
  * @uses CreateQueue
  * @uses JobQueue
  * @uses GetSubscribersFromSegment
  * @uses GetSubscribers
  * @uses RemoveDuplicatesInQueue
  * @uses RemoveBannedEmails
  * @uses RemoveUnsubscribedEmails
  * @uses QueueSize
  * @uses _CalculateBestNewsletter
  * @uses _FinishJob
  * @uses _ActionJob
  * @uses _CalculateBestNewsletter
  *
  * @return Boolean Returns whether the job was processed or not. If a job could not be processed, it returns false. Otherwise it returns true.
  */
 private function _ProcessJob($jobid = 0)
 {
     if ($jobid <= 0) {
         return false;
     }
     $this->_jobid = $jobid;
     /**
      * Load the job, then start it.
      * We need to do this so when we call "StartJob" we can give it the splitid to "start" as well.
      */
     $jobinfo = $this->LoadJob($jobid);
     $jobdetails = $jobinfo['jobdetails'];
     /**
      * Need to load the split campaign
      * before starting the job
      * so if we're in "t"imeout mode,
      * we can look at the stats
      * We also need the weighting's from the split campaign
      * to work it out.
      */
     $this->splitcampaign_details = $this->_splittest_api->Load($jobdetails['splitid']);
     /**
      * If it's a "t"imeout, work out the best stat.
      */
     if ($jobinfo['jobstatus'] == 't') {
         $jobdetails['newsletters'] = $this->_CalculateBestNewsletter($jobdetails['Stats']);
         /**
          * Also need to kill off the "percentage_send_maximum" element
          * as this would cause the job to go into "timeout" mode again.
          */
         unset($jobdetails['percentage_send_maximum']);
     }
     if (!$this->StartJob($jobid, $jobdetails['splitid'])) {
         $this->PauseJob($jobid);
         return false;
     }
     // ----- "Login" to the system as the job's owner.
     $user = GetUser($jobinfo['ownerid']);
     IEM::userLogin($jobinfo['ownerid'], false);
     // -----
     $queueid = false;
     // if there's no queue, start one up.
     if (!($queueid = $this->GetJobQueue($jobid))) {
         /**
          * Randomize the newsletters
          * It's probably already been done but can't hurt to do it again.
          */
         shuffle($jobdetails['newsletters']);
         $sendqueue = $this->CreateQueue('splittest');
         $queueok = $this->JobQueue($jobid, $sendqueue);
         $send_criteria = $jobdetails['SendCriteria'];
         $queueinfo = array('queueid' => $sendqueue, 'queuetype' => 'splittest', 'ownerid' => $jobinfo['ownerid']);
         if (isset($jobdetails['Segments']) && is_array($jobdetails['Segments'])) {
             $this->_subscribers_api->GetSubscribersFromSegment($jobdetails['Segments'], false, $queueinfo, 'nosort');
         } else {
             $this->_subscribers_api->GetSubscribers($send_criteria, array(), false, $queueinfo, $sendqueue);
         }
         if (SENDSTUDIO_DATABASE_TYPE == 'pgsql') {
             $this->Db->OptimizeTable(SENDSTUDIO_TABLEPREFIX . "queues");
         }
         $this->_subscribers_api->RemoveDuplicatesInQueue($sendqueue, 'splittest', $jobdetails['Lists']);
         $this->_subscribers_api->RemoveBannedEmails($jobdetails['Lists'], $sendqueue, 'splittest');
         $this->_subscribers_api->RemoveUnsubscribedEmails($jobdetails['Lists'], $sendqueue, 'splittest');
         if (SENDSTUDIO_DATABASE_TYPE == 'pgsql') {
             $this->Db->OptimizeTable(SENDSTUDIO_TABLEPREFIX . "queues");
         }
         $jobdetails['SendSize'] = $this->_subscribers_api->QueueSize($sendqueue, 'splittest');
         $jobdetails['Stats'] = array();
         $jobdetails['SendQueue'] = $sendqueue;
         /**
          * Delete the old user stats allocations.
          * They were all allocated under one stat/job before so the user recorded their send info
          * so they couldn't set up split test sends and go over their send quota.
          *
          * Now, we need to re-allocate them per newsletter being sent.
          */
         $this->_stats_api->DeleteUserStats($jobinfo['ownerid'], $this->_jobid);
         $statids = array();
         foreach ($jobdetails['newsletters'] as $newsletterid) {
             $newsletterstats = $jobdetails;
             $newsletterstats['Job'] = $jobid;
             $newsletterstats['Queue'] = $sendqueue;
             $newsletterstats['SentBy'] = $queueinfo['ownerid'];
             $newsletterstats['SendType'] = 'splittest';
             $newsletterstats['Newsletter'] = $newsletterid;
             $newsletterstats['Lists'] = $jobdetails['sendingto']['Lists'];
             $newsletterstats['SendCriteria'] = $jobdetails['SendCriteria'];
             $statid = $this->_stats_api->SaveNewsletterStats($newsletterstats);
             $statids[] = $statid;
             $jobdetails['Stats'][$newsletterid] = $statid;
             $this->_stats_api->RecordUserStats($jobinfo['ownerid'], $this->_jobid, $jobdetails['SendSize'], $jobdetails['SendStartTime'], $statid);
         }
         $this->SaveSplitStats($jobdetails['splitid'], $this->_jobid, $statids);
         /**
          * If it's a percentage send,
          * work out the number of emails to send for the first percentage
          * It gets stored in the jobdetails array so it can be saved in the database.
          */
         if ($this->splitcampaign_details['splittype'] == 'percentage') {
             $max_to_email = ceil($this->splitcampaign_details['splitdetails']['percentage'] / 100 * $jobdetails['SendSize']);
             $jobdetails['percentage_send_maximum'] = $max_to_email;
         }
         /**
          * Save the job stat details.
          * Otherwise we could potentially end up with a 'start'ed queue but no stats.
          */
         $this->Set('jobdetails', $jobdetails);
         $this->UpdateJobDetails();
         /**
          * This is to process the 'queueid' later in the code.
          */
         $queueid = $sendqueue;
         // This will make sure that the credit warning emails are also being send out from splittest
         API_USERS::creditEvaluateWarnings($user->GetNewAPI());
     }
     $this->Db->OptimizeTable(SENDSTUDIO_TABLEPREFIX . "queues");
     $queuesize = $this->_subscribers_api->QueueSize($queueid, 'splittest');
     $this->_queueid = $queueid;
     /**
      * If there is a "percentage_send_maximum" variable in the jobdetails array,
      * we are sending to the first part of a 'percentage' split test.
      *
      * We have to send to the rest of the percentage maximum before we pause the job,
      * work out the "best" performing campaign and send to that.
      */
     if (isset($jobdetails['percentage_send_maximum'])) {
         $this->_percentage_send_maximum = (int) $jobdetails['percentage_send_maximum'];
     }
     /**
      * If the _percentage_send_maximum is 0, then "timeout" the job.
      * We must have hit "pause" right at the end of the initial send process or something.
      */
     if ($this->_percentage_send_maximum !== null && $this->_percentage_send_maximum <= 0) {
         $this->TimeoutJob($jobid, $this->splitcampaign_details['splitid'], $this->splitcampaign_details['splitdetails']['hoursafter']);
         IEM::userLogout();
         return true;
     }
     $this->Set('statids', $jobdetails['Stats']);
     $this->Set('jobdetails', $jobdetails);
     $this->Set('jobowner', $jobinfo['ownerid']);
     /**
      * There's nothing left? Just mark it as done.
      */
     if ($queuesize == 0) {
         $this->_FinishJob();
         IEM::userLogout();
         return true;
     }
     $finished = $this->_ActionJob($jobid, $queueid);
     if ($finished) {
         $this->_FinishJob();
     }
     IEM::userLogout();
     return true;
 }