Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?
- /* MMQ -- Mini_Mail_Queue
- *
- * Created by Scott Arciszewski, 24 September 2012 - 27 September 2012
- * kobrasrealm@gmail.com @voodooKobra on Twitter
- */
- class MiniMailQueue {
- const VALIDATION_REGEX = '/[^@]@[A-Za-z0-9\.\-\_]{2,}\.[A-Za-z0-9]{2,}/';
- // Regular expression for email validation
- private $config = array(
- 'mail_temp_folder' => '/tmp/mmq/', // contains a 1 if locked
- 'mail_last_send_file' => '/tmp/mmq/lastSend',
- 'mail_lock_file' => '/tmp/mmq/mailLock', // contains a 1 if locked
- 'mail_master_file' => '/tmp/mmq/master.db', // contains a 1 if locked
- 'mail_send_amount' => 30, // # emails per batch
- 'bytes_per_file' => 20, // # pseudorandom bytes in a filename
- 'mail_send_timeout' => 30 // Time between batches
- ); // Default configuration. Can be changed here or through setopt(array('option' => 'value'));
- private $master; // Master SQLite DB
- public $slave; // Individual SQLite DB
- public function __construct($options=array()) {
- $this->setopt($options);
- if(!file_exists($this->config['mail_master_file'])) {
- $this->createMaster();
- } else {
- $this->master = new PDO('sqlite:'.$this->config['mail_master_file']);
- }
- }
- private function createMaster() {
- // Construct the master DB file if it doesn't exist
- if(!file_exists($this->config['mail_master_file'])) {
- $this->master = new PDO('sqlite:'.$this->config['mail_master_file']);
- chmod($this->config['mail_master_file'], 0777);
- $this->master->exec("CREATE TABLE slaves (filename TEXT, sending INTEGER, birth INTEGER, death INTEGER);");
- }
- }
- public function setopt($options) {
- // Pass an array of new values:
- foreach($options as $i => $v) {
- if(isset($this->config[$i])) $this->config[$i] = $v;
- }
- }
- // $to = array(); of addresses
- // $subject = string
- // $message = string
- // $headers = if array, parsed into string. Otherwise, string
- public function enqueue($to, $subject, $message, $headers) {
- // Create an SQLite database for this jobtask
- // Put the address of this DB into the master db
- $dbFile = $this->randomName(); // bytes
- $dbPath = $this->config['mail_temp_folder'].$dbFile.'.db';
- if(!is_array($to)) die("Must pass an array!");
- if(is_array($headers)) $headers = $this->headerParse($headers);
- $this->slave = new PDO('sqlite:'.$dbPath) or die("ERROR: Could not create file {$dbPath}");
- chmod($dbPath, 0777);
- $this->master->exec("INSERT INTO slaves (filename, sending, birth, death) VALUES ('{$dbFile}', '1', '".time()."', '0');");
- // Now, creat the table structures for the slave
- $this->slave->exec("CREATE TABLE metadata (subject TEXT, message TEXT, headers TEXT);");
- $this->slave->exec("CREATE TABLE recipients (address TEXT, received TEXT, time INTEGER);");
- // Prepare statements, then execute. SQLi be damned
- $st = $this->slave->prepare("INSERT INTO metadata (subject, message, headers) VALUES (?, ?, ?);");
- $st->execute( array($subject, $message, $headers) );
- $st = $this->slave->prepare("INSERT INTO recipients (address, received, time) VALUES (?, '0', '0');");
- // Wonderful! Now to add data:
- foreach($to as $t) {
- $st->execute(array($t));
- }
- // Now we should be done.
- return true;
- }
- protected function headerParse($array) {
- // Parse into headers
- $head = '';
- foreach($array as $i => $v) {
- $head .= "{$i}: {$v}\r\n";
- }
- return trim($head);
- }
- public function sendBatch() {
- // Send ____ emails
- // First step: Lock from double-changing
- file_put_contents($this->config['mail_lock_file'], 1);
- $sl = array();
- foreach($this->master->query("SELECT ROWID as id, filename, sending, birth, death FROM slaves WHERE sending = '1'") as $slv) {
- $sl[] = new MMQSlave($slv);
- }
- if(empty($sl)) return false;
- $N = count($sl);
- $L = $this->config['mail_send_amount'];
- $LN = $L / ($N > 1 ? $N : 1);
- foreach($sl as $s) {
- $dbPath = $this->config['mail_temp_folder'].$s->filename.'.db';
- $slave = new PDO('sqlite:'.$dbPath);
- $batch = $slave->query("SELECT * FROM metadata");
- $meta = $batch->fetch(PDO::FETCH_ASSOC); // Pull metadata
- $j = 0;
- foreach($slave->query("SELECT ROWID AS id, address, received, time FROM recipients WHERE received = '0' LIMIT 0, {$LN}") as $r) {
- mail($r['address'], $meta['subject'], $meta['message'], $meta['headers']);
- // Send the email
- $slave->exec("UPDATE recipients SET received = '1', time = '".time()."' WHERE ROWID = '{$r['id']}'");
- // Mark it as sent
- $j++;
- }
- if($j < $LN) {
- // We ran out of recipients!
- $this->master->exec("UPDATE slaves SET sending = '0', death = '".time()."' WHERE id = '{$s->id}'");
- }
- $LN = ($LN - $j) + ($L / ($N > 1 ? $N : 1));
- // If batch A only had 3 left to send and batch B has 27 left to send, still send them all this tick.
- }
- // Last step:
- file_put_contents($this->config['mail_lock_file'], 0);
- file_put_contents($this->config['mail_last_send_file'], time());
- }
- public function ready($autosend=false) {
- // Is the file locked?
- $locked = intval(file_get_contents($this->config['mail_lock_file']));
- if($locked) return false;
- // Has it been more than ____ seconds?
- $lastsent = intval(file_get_contents($this->config['mail_last_send_file']));
- if(time() - $lastsent < $this->config['mail_send_timeout']) return false;
- // Yes, you can send something
- if($autosend) return $this->sendBatch(); // Send a batch, if not told otherwise
- return true;
- }
- public function randomName() {
- // Generate a random filename for this database!
- if(function_exists('bin2hex') && function_exists('openssl_random_pseudo_bytes')) {
- // Strong randomization!
- return $this->convBase(
- bin2hex(openssl_random_pseudo_bytes($this->config['bytes_per_file'])),
- '0123456789abcdef',
- 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_' // Non-standard base64
- );
- } else {
- if(is_readable('/dev/urandom')) {
- // Screw you, PHP < 4.0! We'll do things my way.
- $fp = fopen('/dev/urandom', 'rb');
- list(, $hex) = unpack('H*', fread($fp, $this->config['bytes_per_file']));
- fclose($fp);
- return $this->convBase(
- bin2hex($hex),
- '0123456789abcdef',
- 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_' // Non-standard base64
- );
- } else { // Weak randomization!
- $acc = ''; // Accumulator
- $target = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_';
- while(strlen($acc) < $this->config['bytes_per_file']) {
- $acc .= $target[mt_rand(0, 63)];
- }
- return $acc;
- // Weaksauce, man
- }
- }
- }
- /*
- ATTENTION!
- /\ /\ /\ /\ /\ /\ /\ /\
- /##\ /##\ /##\ /##\ /##\ /##\ /##\ /##\
- /####\ /####\ /####\ /####\ /####\ /####\ /####\ /####\
- /######\ /######\ /######\ /######\ /######\ /######\ /######\ /######\
- \######/ \######/ \######/ \######/ \######/ \######/ \######/ \######/
- \####/ \####/ \####/ \####/ \####/ \####/ \####/ \####/
- \##/ \##/ \##/ \##/ \##/ \##/ \##/ \##/
- \/ \/ \/ \/ \/ \/ \/ \/
- BELOW IS AUXULLIARY AND NOT WRITTEN BY SCOTT
- */
- public function convBase($numberInput, $fromBaseInput, $toBaseInput) {
- // http://php.net/manual/en/function.base-convert.php#106546
- if ($fromBaseInput==$toBaseInput) return $numberInput;
- $fromBase = str_split($fromBaseInput,1);
- $toBase = str_split($toBaseInput,1);
- $number = str_split($numberInput,1);
- $fromLen=strlen($fromBaseInput);
- $toLen=strlen($toBaseInput);
- $numberLen=strlen($numberInput);
- $retval='';
- if ($toBaseInput == '0123456789')
- {
- $retval=0;
- for ($i = 1;$i <= $numberLen; $i++)
- $retval = bcadd($retval, bcmul(array_search($number[$i-1], $fromBase),bcpow($fromLen,$numberLen-$i)));
- return $retval;
- }
- if ($fromBaseInput != '0123456789')
- $base10=$this->convBase($numberInput, $fromBaseInput, '0123456789');
- else
- $base10 = $numberInput;
- if ($base10<strlen($toBaseInput))
- return $toBase[$base10];
- while($base10 != '0')
- {
- $retval = $toBase[bcmod($base10,$toLen)].$retval;
- $base10 = bcdiv($base10,$toLen,0);
- }
- return $retval;
- }
- }
- /*
- * MMQSlave :: Data structure for each recipient
- */
- class MMQSlave {
- public $id;
- public $filename;
- public $sending;
- public $birth;
- public $death;
- public function __construct($sl) {
- $this->id = $sl['id'];
- $this->filename = $sl['filename'];
- $this->sending = $sl['sending'];
- $this->birth = $sl['birth'];
- $this->death = $sl['death'];
- }
- }
- ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement