1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4: */ 3/** 4 * +----------------------------------------------------------------------+ 5 * | PEAR :: Mail :: Queue | 6 * +----------------------------------------------------------------------+ 7 * | Copyright (c) 1997-2008 Radek Maciaszek, Lorenzo Alberton | 8 * +----------------------------------------------------------------------+ 9 * | All rights reserved. | 10 * | | 11 * | Redistribution and use in source and binary forms, with or without | 12 * | modification, are permitted provided that the following conditions | 13 * | are met: | 14 * | | 15 * | * Redistributions of source code must retain the above copyright | 16 * | notice, this list of conditions and the following disclaimer. | 17 * | * Redistributions in binary form must reproduce the above copyright | 18 * | notice, this list of conditions and the following disclaimer in | 19 * | the documentation and/or other materials provided with the | 20 * | distribution. | 21 * | * The names of its contributors may be used to endorse or promote | 22 * | products derived from this software without specific prior written | 23 * | permission. | 24 * | | 25 * | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 26 * | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 27 * | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 28 * | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | 29 * | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | 30 * | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | 31 * | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 32 * | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 33 * | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 34 * | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | 35 * | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 36 * | POSSIBILITY OF SUCH DAMAGE. | 37 * +----------------------------------------------------------------------+ 38 * 39 * PHP Version 4 and 5 40 * 41 * @category Mail 42 * @package Mail_Queue 43 * @author Radek Maciaszek <chief@php.net> 44 * @author Lorenzo Alberton <l.alberton@quipo.it> 45 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License 46 * @version CVS: $Id: Queue.php 309130 2011-03-11 17:34:24Z till $ 47 * @link http://pear.php.net/package/Mail_Queue 48 */ 49 50/** 51 * This is special constant define start offset for limit sql queries to 52 * get mails. 53 */ 54define('MAILQUEUE_START', 0); 55 56/** 57 * You can specify how many mails will be loaded to 58 * queue else object use this constant for load all mails from db. 59 */ 60define('MAILQUEUE_ALL', -1); 61 62/** 63 * When you put new mail to queue you could specify user id who send e-mail. 64 * Else you could use system id: MAILQUEUE_SYSTEM or user unknown id: MAILQUEUE_UNKNOWN 65 */ 66define('MAILQUEUE_SYSTEM', -1); 67define('MAILQUEUE_UNKNOWN', -2); 68 69/** 70 * This constant tells Mail_Queue how many times should try 71 * to send mails again if was any errors before. 72 */ 73define('MAILQUEUE_TRY', 25); 74 75/** 76 * MAILQUEUE_ERROR constants 77 */ 78define('MAILQUEUE_ERROR', -1); 79define('MAILQUEUE_ERROR_NO_DRIVER', -2); 80define('MAILQUEUE_ERROR_NO_CONTAINER', -3); 81define('MAILQUEUE_ERROR_CANNOT_INITIALIZE', -4); 82define('MAILQUEUE_ERROR_NO_OPTIONS', -5); 83define('MAILQUEUE_ERROR_CANNOT_CONNECT', -6); 84define('MAILQUEUE_ERROR_QUERY_FAILED', -7); 85define('MAILQUEUE_ERROR_UNEXPECTED', -8); 86define('MAILQUEUE_ERROR_CANNOT_SEND_MAIL', -9); 87define('MAILQUEUE_ERROR_NO_RECIPIENT', -10); 88define('MAILQUEUE_ERROR_UNKNOWN_CONTAINER', -11); 89 90/** 91 * PEAR 92 * @ignore 93 */ 94require_once 'PEAR.php'; 95 96/** 97 * Mail 98 * @ignore 99 */ 100require_once 'Mail.php'; 101 102/** 103 * Mail_mime 104 * @ignore 105 */ 106require_once 'Mail/mime.php'; 107 108/** 109 * Mail_Queue_Error 110 */ 111require_once 'Mail/Queue/Error.php'; 112 113 114/** 115 * Mail_Queue - base class for mail queue managment. 116 * 117 * @category Mail 118 * @package Mail_Queue 119 * @author Radek Maciaszek <chief@php.net> 120 * @author Lorenzo Alberton <l.alberton@quipo.it> 121 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License 122 * @version Release: @package_version@ 123 * @link http://pear.php.net/package/Mail_Queue 124 */ 125class Mail_Queue extends PEAR 126{ 127 // {{{ Class vars 128 129 /** 130 * Mail options: smtp, mail etc. see Mail::factory 131 * 132 * @var array 133 */ 134 var $mail_options; 135 136 /** 137 * Mail_Queue_Container 138 * 139 * @var object 140 */ 141 var $container; 142 143 /** 144 * Reference to Pear_Mail object 145 * 146 * @var object 147 */ 148 var $send_mail; 149 150 /** 151 * Pear error mode (when raiseError is called) 152 * (see PEAR doc) 153 * 154 * @var int $_pearErrorMode 155 * @access private 156 */ 157 var $pearErrorMode = PEAR_ERROR_RETURN; 158 159 /** 160 * To catch errors in construct 161 * @var array 162 * @see self::Mail_Queue() 163 * @see self::factory() 164 * @see self::hasErrors() 165 * @access private 166 */ 167 var $_initErrors = array(); 168 169 // }}} 170 // {{{ __construct 171 172 function __construct($container_options, $mail_options) 173 { 174 return $this->Mail_Queue($container_options, $mail_options); 175 } 176 177 // }}} 178 // {{{ Mail_Queue 179 180 /** 181 * Mail_Queue constructor 182 * 183 * @param array $container_options Mail_Queue container options 184 * @param array $mail_options How send mails. 185 * 186 * @return Mail_Queue 187 * 188 * @access public 189 * @deprecated 190 */ 191 function Mail_Queue($container_options, $mail_options) 192 { 193 $this->PEAR(); 194 if (isset($mail_options['pearErrorMode'])) { 195 $this->pearErrorMode = $mail_options['pearErrorMode']; 196 // ugly hack to propagate 'pearErrorMode' 197 $container_options['pearErrorMode'] = $mail_options['pearErrorMode']; 198 } 199 200 if (!is_array($mail_options) || !isset($mail_options['driver'])) { 201 array_push($this->_initErrors, new Mail_Queue_Error(MAILQUEUE_ERROR_NO_DRIVER, 202 $this->pearErrorMode, E_USER_ERROR, __FILE__, __LINE__)); 203 } 204 $this->mail_options = $mail_options; 205 206 if (!is_array($container_options) || !isset($container_options['type'])) { 207 array_push($this->_initErrors, new Mail_Queue_Error(MAILQUEUE_ERROR_NO_CONTAINER, 208 $this->pearErrorMode, E_USER_ERROR, __FILE__, __LINE__)); 209 } 210 $container_type = strtolower($container_options['type']); 211 $container_class = 'Mail_Queue_Container_' . $container_type; 212 $container_classfile = $container_type . '.php'; 213 214 // Attempt to include a custom version of the named class, but don't treat 215 // a failure as fatal. The caller may have already included their own 216 // version of the named class. 217 if (!class_exists($container_class)) { 218 include_once 'Mail/Queue/Container/' . $container_classfile; 219 } 220 if (!class_exists($container_class)) { 221 array_push($this->_initErrors, new Mail_Queue_Error(MAILQUEUE_ERROR_UNKNOWN_CONTAINER, 222 $this->pearErrorMode, E_USER_ERROR, __FILE__, __LINE__)); 223 } else { 224 225 unset($container_options['type']); 226 227 $this->container = new $container_class($container_options); 228 if(PEAR::isError($this->container)) { 229 array_push($this->_initErrors, new Mail_Queue_Error(MAILQUEUE_ERROR_CANNOT_INITIALIZE, 230 $this->pearErrorMode, E_USER_ERROR, __FILE__, __LINE__)); 231 } 232 } 233 } 234 235 /** 236 * Factory is used to initialize Mail_Queue, this is necessary to catch possible 237 * errors during the initialization. 238 * 239 * @param array $container_options Options for the container. 240 * @param array $mail_options Options for mail. 241 * 242 * @return mixed Mail_Queue|Mail_Queue_Error 243 * @see self::Mail_Queue() 244 * @since 1.2.3 245 */ 246 function factory($container_options, $mail_options) 247 { 248 $obj = new Mail_Queue($container_options, $mail_options); 249 if ($obj->hasErrors()) { 250 /** 251 * @see self::getErrors() 252 */ 253 return new Mail_Queue_Error(MAILQUEUE_ERROR, 254 $this->pearErrorMode, E_USER_ERROR, __FILE__, __LINE__); 255 } 256 return $obj; 257 } 258 // }}} 259 // {{{ _Mail_Queue() 260 261 /** 262 * Mail_Queue desctructor 263 * 264 * @return void 265 * @access public 266 */ 267 function _Mail_Queue() 268 { 269 unset($this); 270 } 271 272 // }}} 273 // {{{ __destruct 274 275 function __destruct() 276 { 277 $this->_Mail_Queue(); 278 } 279 280 // }}} 281 // {{{ factorySendMail() 282 283 /** 284 * Provides an interface for generating Mail:: objects of various 285 * types see Mail::factory() 286 * 287 * @return void 288 * 289 * @access public 290 */ 291 function factorySendMail() 292 { 293 $options = $this->mail_options; 294 unset($options['driver']); 295 296 $this->send_mail =& Mail::factory($this->mail_options['driver'], $options); 297 } 298 299 /** 300 * Returns the number of emails currently in the queue. 301 * 302 * @return int 303 */ 304 function getQueueCount() 305 { 306 if (!is_a($this->container, 'mail_queue_container')) { 307 array_push( 308 $this->_initErrors, 309 new Mail_Queue_Error( 310 MAILQUEUE_ERROR_NO_CONTAINER, 311 $this->pearErrorMode, 312 E_USER_ERROR, 313 __FILE__, 314 __LINE__ 315 ) 316 ); 317 return 0; 318 } 319 return $this->container->getQueueCount(); 320 } 321 322 // }}} 323 // {{{ setBufferSize() 324 325 /** 326 * Keep memory usage under control. You can set the max number 327 * of mails that can be in the preload buffer at any given time. 328 * It won't limit the number of mails you can send, just the 329 * internal buffer size. 330 * 331 * @param integer $size Optional - internal preload buffer size 332 */ 333 function setBufferSize($size = 10) 334 { 335 $this->container->buffer_size = $size; 336 } 337 338 339 // }}} 340 // {{{ sendMailsInQueue() 341 342 /** 343 * Send mails fom queue. 344 * 345 * Mail_Queue::sendMailsInQueue() 346 * 347 * @param integer $limit Optional - max limit mails send. 348 * This is the max number of emails send by 349 * this function. 350 * @param integer $offset Optional - you could load mails from $offset (by id) 351 * @param integer $try Optional - hoh many times mailqueu should try send 352 * each mail. If mail was sent succesful it will be 353 * deleted from Mail_Queue. 354 * @param mixed $callback Optional, a callback (string or array) to save the 355 * SMTP ID and the SMTP greeting. 356 * 357 * @return mixed True on success else MAILQUEUE_ERROR object. 358 */ 359 function sendMailsInQueue($limit = MAILQUEUE_ALL, $offset = MAILQUEUE_START, 360 $try = MAILQUEUE_TRY, $callback = null) 361 { 362 if (!is_int($limit) || !is_int($offset) || !is_int($try)) { 363 return Mail_Queue::raiseError( 364 "sendMailsInQueue(): limit, offset and try must be integer.", 365 MAILQUEUE_ERROR_UNEXPECTED 366 ); 367 } 368 369 if ($callback !== null) { 370 if (!is_array($callback) && !is_string($callback)) { 371 return Mail_Queue::raiseError( 372 "sendMailsInQueue(): callback must be a string or an array.", 373 MAILQUEUE_ERROR_UNEXPECTED 374 ); 375 } 376 } 377 378 $this->container->setOption($limit, $offset, $try); 379 while (($mail = $this->get()) && !PEAR::isError($mail)) { 380 $this->container->countSend($mail); 381 382 $result = $this->sendMail($mail, true); 383 if (PEAR::isError($result)) { 384 //remove the problematic mail from the buffer, but don't delete 385 //it from the db: it might be a temporary issue. 386 $this->container->skip(); 387 PEAR::raiseError( 388 'Error in sending mail: '.$result->getMessage(), 389 MAILQUEUE_ERROR_CANNOT_SEND_MAIL, PEAR_ERROR_TRIGGER, 390 E_USER_NOTICE 391 ); 392 continue; 393 } 394 395 //take care of callback first, as it may need to retrieve extra data 396 //from the mail_queue table. 397 if ($callback !== null) { 398 $queued_as = null; 399 $greeting = null; 400 if (isset($this->queued_as)) { 401 $queued_as = $this->queued_as; 402 } 403 if (isset($this->greeting)) { 404 $greeting = $this->greeting; 405 } 406 call_user_func($callback, 407 array('id' => $mail->getId(), 408 'queued_as' => $queued_as, 409 'greeting' => $greeting)); 410 } 411 412 // delete email from queue? 413 if ($mail->isDeleteAfterSend()) { 414 $status = $this->deleteMail($mail->getId()); 415 } 416 417 unset($mail); 418 419 if (isset($this->mail_options['delay']) 420 && $this->mail_options['delay'] > 0) { 421 sleep($this->mail_options['delay']); 422 } 423 } 424 425 // most likely from breaking the loop 426 if (isset($mail) && PEAR::isError($mail)) { 427 return $mail; 428 } 429 430 if (!empty($this->mail_options['persist']) && is_object($this->send_mail)) { 431 $this->send_mail->disconnect(); 432 } 433 return true; 434 } 435 436 // }}} 437 // {{{ sendMailById() 438 439 /** 440 * Send Mail by $id identifier. (bypass Mail_Queue) 441 * 442 * @param integer $id Mail identifier 443 * @param bool $set_as_sent 444 * @return mixed boolean: true on success else false, or PEAR_Error 445 * 446 * @access public 447 * @uses self::sendMail() 448 */ 449 function sendMailById($id, $set_as_sent=true) 450 { 451 $mail =& $this->container->getMailById($id); 452 if (PEAR::isError($mail)) { 453 return $mail; 454 } 455 return $this->sendMail($mail, $set_as_sent); 456 } 457 458 // }}} 459 // {{{ sendMail() 460 461 /** 462 * Send mail from {@link Mail_Queue_Body} object 463 * 464 * @param object Mail_Queue_Body object 465 * @return mixed True on success else pear error class 466 * @param bool $set_as_sent 467 * 468 * @access public 469 * @see self::sendMailById() 470 */ 471 function sendMail($mail, $set_as_sent=true) 472 { 473 if (!is_a($mail, 'Mail_Queue_Body')) { 474 if (is_a($mail, 'Mail_Queue_Error')) { 475 return $mail; 476 } 477 return Mail_Queue_Error( 478 "Unknown object/type: " . get_class($mail), 479 MAILQUEUE_ERROR_UNEXPECTED 480 ); 481 } 482 $recipient = $mail->getRecipient(); 483 if (empty($recipient)) { 484 return new Mail_Queue_Error('Recipient cannot be empty.', 485 MAILQUEUE_ERROR_NO_RECIPIENT); 486 } 487 488 $hdrs = $mail->getHeaders(); 489 $body = $mail->getBody(); 490 491 if (empty($this->send_mail)) { 492 $this->factorySendMail(); 493 } 494 if (PEAR::isError($this->send_mail)) { 495 return $this->send_mail; 496 } 497 $sent = $this->send_mail->send($recipient, $hdrs, $body); 498 if (!PEAR::isError($sent) && $sent && $set_as_sent) { 499 $this->container->setAsSent($mail); 500 } 501 if (isset($this->send_mail->queued_as)) { 502 $this->queued_as = $this->send_mail->queued_as; 503 } 504 if (isset($this->send_mail->greeting)) { 505 $this->greeting = $this->send_mail->greeting; 506 } 507 return $sent; 508 } 509 510 // }}} 511 // {{{ get() 512 513 /** 514 * Get next mail from queue. The emails are preloaded 515 * in a buffer for better performances. 516 * 517 * @return object Mail_Queue_Container or error object 518 * @throw MAILQUEUE_ERROR 519 * @access public 520 */ 521 function get() 522 { 523 return $this->container->get(); 524 } 525 526 // }}} 527 // {{{ put() 528 529 /** 530 * Put new mail in queue. 531 * 532 * @see Mail_Queue_Container::put() 533 * 534 * @param string $time_to_send When mail have to be send 535 * @param integer $id_user Sender id 536 * @param string $ip Sender ip 537 * @param string $from Sender e-mail 538 * @param string|array $to Reciepient(s) e-mail 539 * @param string $hdrs Mail headers (in RFC) 540 * @param string $body Mail body (in RFC) 541 * @return mixed ID of the record where this mail has been put 542 * or Mail_Queue_Error on error 543 * 544 * @access public 545 */ 546 function put($from, $to, $hdrs, $body, $sec_to_send=0, $delete_after_send=true, $id_user=MAILQUEUE_SYSTEM) 547 { 548 $ip = getenv('REMOTE_ADDR'); 549 550 $time_to_send = date("Y-m-d H:i:s", time() + $sec_to_send); 551 552 return $this->container->put( 553 $time_to_send, 554 $id_user, 555 $ip, 556 $from, 557 serialize($to), 558 serialize($hdrs), 559 serialize($body), 560 $delete_after_send 561 ); 562 } 563 564 // }}} 565 // {{{ deleteMail() 566 567 /** 568 * Delete mail from queue database 569 * 570 * @param integer $id Maila identifier 571 * @return boolean 572 * 573 * @access private 574 */ 575 function deleteMail($id) 576 { 577 return $this->container->deleteMail($id); 578 } 579 580 // }}} 581 // {{{ isError() 582 583 /** 584 * Tell whether a result code from a Mail_Queue method is an error 585 * 586 * @param int $value result code 587 * @return boolean whether $value is an MAILQUEUE_ERROR 588 * @access public 589 */ 590 static function isError($value) 591 { 592 return (is_object($value) && is_a($value, 'pear_error')); 593 } 594 595 // }}} 596 // {{{ errorMessage() 597 598 /** 599 * Return a textual error message for a MDB error code 600 * 601 * @param int $value error code 602 * @return string error message, or false if the error code was 603 * not recognized 604 * @access public 605 */ 606 function errorMessage($value) 607 { 608 static $errorMessages; 609 if (!isset($errorMessages)) { 610 $errorMessages = array( 611 MAILQUEUE_ERROR => 'unknown error', 612 MAILQUEUE_ERROR_NO_DRIVER => 'No mail driver specified', 613 MAILQUEUE_ERROR_NO_CONTAINER => 'No container specified', 614 MAILQUEUE_ERROR_CANNOT_INITIALIZE => 'Cannot initialize container', 615 MAILQUEUE_ERROR_NO_OPTIONS => 'No container options specified', 616 MAILQUEUE_ERROR_CANNOT_CONNECT => 'Cannot connect to database', 617 MAILQUEUE_ERROR_QUERY_FAILED => 'db query failed', 618 MAILQUEUE_ERROR_UNEXPECTED => 'Unexpected class', 619 MAILQUEUE_ERROR_CANNOT_SEND_MAIL => 'Cannot send email', 620 ); 621 } 622 623 if (Mail_Queue::isError($value)) { 624 $value = $value->getCode(); 625 } 626 627 return isset($errorMessages[$value]) ? 628 $errorMessages[$value] : $errorMessages[MAILQUEUE_ERROR]; 629 } 630 631 // }}} 632 633 /** 634 * hasErrors() returns true/false, if self::$_initErrors are populated. 635 * 636 * @return boolean 637 * @see self::Mail_Queue 638 * @see self::$_initErrors 639 * @see self::getErrors() 640 * @since 1.2.3 641 */ 642 function hasErrors() 643 { 644 if (count($this->_initErrors) > 0) { 645 return true; 646 } 647 return false; 648 } 649 650 /** 651 * getErrors() returns the errors. 652 * 653 * @return array 654 * @see self::Mail_Queue 655 * @see self::$_initErrors 656 * @see self::hasErrors() 657 * @since 1.2.3 658 */ 659 function getErrors() 660 { 661 return $this->_initErrors; 662 } 663 664/* 665 function raiseError($msg, $code = null, $file = null, $line = null, $mode = null) 666 { 667 if ($file !== null) { 668 $err = PEAR::raiseError(sprintf("%s [%s on line %d].", $msg, $file, $line), $code, $mode); 669 } else { 670 $err = PEAR::raiseError(sprintf("%s", $msg), $code, $mode); 671 } 672� � � � return $err; 673 } 674*/ 675} 676?> 677