PageRenderTime 179ms CodeModel.GetById 71ms app.highlight 76ms RepoModel.GetById 14ms app.codeStats 1ms

/magehelp/system/libraries/Email.php

https://bitbucket.org/jit_bec/shopifine
PHP | 2092 lines | 1546 code | 247 blank | 299 comment | 150 complexity | d26e2f86b4f34f29726a8ccefaea96dc MD5 | raw file
   1<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
   2/**
   3 * CodeIgniter
   4 *
   5 * An open source application development framework for PHP 5.1.6 or newer
   6 *
   7 * @package		CodeIgniter
   8 * @author		ExpressionEngine Dev Team
   9 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
  10 * @license		http://codeigniter.com/user_guide/license.html
  11 * @link		http://codeigniter.com
  12 * @since		Version 1.0
  13 * @filesource
  14 */
  15
  16// ------------------------------------------------------------------------
  17
  18/**
  19 * CodeIgniter Email Class
  20 *
  21 * Permits email to be sent using Mail, Sendmail, or SMTP.
  22 *
  23 * @package		CodeIgniter
  24 * @subpackage	Libraries
  25 * @category	Libraries
  26 * @author		ExpressionEngine Dev Team
  27 * @link		http://codeigniter.com/user_guide/libraries/email.html
  28 */
  29class CI_Email {
  30
  31	var	$useragent		= "CodeIgniter";
  32	var	$mailpath		= "/usr/sbin/sendmail";	// Sendmail path
  33	var	$protocol		= "mail";	// mail/sendmail/smtp
  34	var	$smtp_host		= "";		// SMTP Server.  Example: mail.earthlink.net
  35	var	$smtp_user		= "";		// SMTP Username
  36	var	$smtp_pass		= "";		// SMTP Password
  37	var	$smtp_port		= "25";		// SMTP Port
  38	var	$smtp_timeout	= 5;		// SMTP Timeout in seconds
  39	var	$smtp_crypto	= "";		// SMTP Encryption. Can be null, tls or ssl.
  40	var	$wordwrap		= TRUE;		// TRUE/FALSE  Turns word-wrap on/off
  41	var	$wrapchars		= "76";		// Number of characters to wrap at.
  42	var	$mailtype		= "text";	// text/html  Defines email formatting
  43	var	$charset		= "utf-8";	// Default char set: iso-8859-1 or us-ascii
  44	var	$multipart		= "mixed";	// "mixed" (in the body) or "related" (separate)
  45	var $alt_message	= '';		// Alternative message for HTML emails
  46	var	$validate		= FALSE;	// TRUE/FALSE.  Enables email validation
  47	var	$priority		= "3";		// Default priority (1 - 5)
  48	var	$newline		= "\n";		// Default newline. "\r\n" or "\n" (Use "\r\n" to comply with RFC 822)
  49	var $crlf			= "\n";		// The RFC 2045 compliant CRLF for quoted-printable is "\r\n".  Apparently some servers,
  50									// even on the receiving end think they need to muck with CRLFs, so using "\n", while
  51									// distasteful, is the only thing that seems to work for all environments.
  52	var $send_multipart	= TRUE;		// TRUE/FALSE - Yahoo does not like multipart alternative, so this is an override.  Set to FALSE for Yahoo.
  53	var	$bcc_batch_mode	= FALSE;	// TRUE/FALSE  Turns on/off Bcc batch feature
  54	var	$bcc_batch_size	= 200;		// If bcc_batch_mode = TRUE, sets max number of Bccs in each batch
  55	var $_safe_mode		= FALSE;
  56	var	$_subject		= "";
  57	var	$_body			= "";
  58	var	$_finalbody		= "";
  59	var	$_alt_boundary	= "";
  60	var	$_atc_boundary	= "";
  61	var	$_header_str	= "";
  62	var	$_smtp_connect	= "";
  63	var	$_encoding		= "8bit";
  64	var $_IP			= FALSE;
  65	var	$_smtp_auth		= FALSE;
  66	var $_replyto_flag	= FALSE;
  67	var	$_debug_msg		= array();
  68	var	$_recipients	= array();
  69	var	$_cc_array		= array();
  70	var	$_bcc_array		= array();
  71	var	$_headers		= array();
  72	var	$_attach_name	= array();
  73	var	$_attach_type	= array();
  74	var	$_attach_disp	= array();
  75	var	$_protocols		= array('mail', 'sendmail', 'smtp');
  76	var	$_base_charsets	= array('us-ascii', 'iso-2022-');	// 7-bit charsets (excluding language suffix)
  77	var	$_bit_depths	= array('7bit', '8bit');
  78	var	$_priorities	= array('1 (Highest)', '2 (High)', '3 (Normal)', '4 (Low)', '5 (Lowest)');
  79
  80
  81	/**
  82	 * Constructor - Sets Email Preferences
  83	 *
  84	 * The constructor can be passed an array of config values
  85	 */
  86	public function __construct($config = array())
  87	{
  88		if (count($config) > 0)
  89		{
  90			$this->initialize($config);
  91		}
  92		else
  93		{
  94			$this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
  95			$this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;
  96		}
  97
  98		log_message('debug', "Email Class Initialized");
  99	}
 100
 101	// --------------------------------------------------------------------
 102
 103	/**
 104	 * Initialize preferences
 105	 *
 106	 * @access	public
 107	 * @param	array
 108	 * @return	void
 109	 */
 110	public function initialize($config = array())
 111	{
 112		foreach ($config as $key => $val)
 113		{
 114			if (isset($this->$key))
 115			{
 116				$method = 'set_'.$key;
 117
 118				if (method_exists($this, $method))
 119				{
 120					$this->$method($val);
 121				}
 122				else
 123				{
 124					$this->$key = $val;
 125				}
 126			}
 127		}
 128		$this->clear();
 129
 130		$this->_smtp_auth = ($this->smtp_user == '' AND $this->smtp_pass == '') ? FALSE : TRUE;
 131		$this->_safe_mode = ((boolean)@ini_get("safe_mode") === FALSE) ? FALSE : TRUE;
 132
 133		return $this;
 134	}
 135
 136	// --------------------------------------------------------------------
 137
 138	/**
 139	 * Initialize the Email Data
 140	 *
 141	 * @access	public
 142	 * @return	void
 143	 */
 144	public function clear($clear_attachments = FALSE)
 145	{
 146		$this->_subject		= "";
 147		$this->_body		= "";
 148		$this->_finalbody	= "";
 149		$this->_header_str	= "";
 150		$this->_replyto_flag = FALSE;
 151		$this->_recipients	= array();
 152		$this->_cc_array	= array();
 153		$this->_bcc_array	= array();
 154		$this->_headers		= array();
 155		$this->_debug_msg	= array();
 156
 157		$this->_set_header('User-Agent', $this->useragent);
 158		$this->_set_header('Date', $this->_set_date());
 159
 160		if ($clear_attachments !== FALSE)
 161		{
 162			$this->_attach_name = array();
 163			$this->_attach_type = array();
 164			$this->_attach_disp = array();
 165		}
 166
 167		return $this;
 168	}
 169
 170	// --------------------------------------------------------------------
 171
 172	/**
 173	 * Set FROM
 174	 *
 175	 * @access	public
 176	 * @param	string
 177	 * @param	string
 178	 * @return	void
 179	 */
 180	public function from($from, $name = '')
 181	{
 182		if (preg_match( '/\<(.*)\>/', $from, $match))
 183		{
 184			$from = $match['1'];
 185		}
 186
 187		if ($this->validate)
 188		{
 189			$this->validate_email($this->_str_to_array($from));
 190		}
 191
 192		// prepare the display name
 193		if ($name != '')
 194		{
 195			// only use Q encoding if there are characters that would require it
 196			if ( ! preg_match('/[\200-\377]/', $name))
 197			{
 198				// add slashes for non-printing characters, slashes, and double quotes, and surround it in double quotes
 199				$name = '"'.addcslashes($name, "\0..\37\177'\"\\").'"';
 200			}
 201			else
 202			{
 203				$name = $this->_prep_q_encoding($name, TRUE);
 204			}
 205		}
 206
 207		$this->_set_header('From', $name.' <'.$from.'>');
 208		$this->_set_header('Return-Path', '<'.$from.'>');
 209
 210		return $this;
 211	}
 212
 213	// --------------------------------------------------------------------
 214
 215	/**
 216	 * Set Reply-to
 217	 *
 218	 * @access	public
 219	 * @param	string
 220	 * @param	string
 221	 * @return	void
 222	 */
 223	public function reply_to($replyto, $name = '')
 224	{
 225		if (preg_match( '/\<(.*)\>/', $replyto, $match))
 226		{
 227			$replyto = $match['1'];
 228		}
 229
 230		if ($this->validate)
 231		{
 232			$this->validate_email($this->_str_to_array($replyto));
 233		}
 234
 235		if ($name == '')
 236		{
 237			$name = $replyto;
 238		}
 239
 240		if (strncmp($name, '"', 1) != 0)
 241		{
 242			$name = '"'.$name.'"';
 243		}
 244
 245		$this->_set_header('Reply-To', $name.' <'.$replyto.'>');
 246		$this->_replyto_flag = TRUE;
 247
 248		return $this;
 249	}
 250
 251	// --------------------------------------------------------------------
 252
 253	/**
 254	 * Set Recipients
 255	 *
 256	 * @access	public
 257	 * @param	string
 258	 * @return	void
 259	 */
 260	public function to($to)
 261	{
 262		$to = $this->_str_to_array($to);
 263		$to = $this->clean_email($to);
 264
 265		if ($this->validate)
 266		{
 267			$this->validate_email($to);
 268		}
 269
 270		if ($this->_get_protocol() != 'mail')
 271		{
 272			$this->_set_header('To', implode(", ", $to));
 273		}
 274
 275		switch ($this->_get_protocol())
 276		{
 277			case 'smtp'		:
 278				$this->_recipients = $to;
 279			break;
 280			case 'sendmail'	:
 281			case 'mail'		:
 282				$this->_recipients = implode(", ", $to);
 283			break;
 284		}
 285
 286		return $this;
 287	}
 288
 289	// --------------------------------------------------------------------
 290
 291	/**
 292	 * Set CC
 293	 *
 294	 * @access	public
 295	 * @param	string
 296	 * @return	void
 297	 */
 298	public function cc($cc)
 299	{
 300		$cc = $this->_str_to_array($cc);
 301		$cc = $this->clean_email($cc);
 302
 303		if ($this->validate)
 304		{
 305			$this->validate_email($cc);
 306		}
 307
 308		$this->_set_header('Cc', implode(", ", $cc));
 309
 310		if ($this->_get_protocol() == "smtp")
 311		{
 312			$this->_cc_array = $cc;
 313		}
 314
 315		return $this;
 316	}
 317
 318	// --------------------------------------------------------------------
 319
 320	/**
 321	 * Set BCC
 322	 *
 323	 * @access	public
 324	 * @param	string
 325	 * @param	string
 326	 * @return	void
 327	 */
 328	public function bcc($bcc, $limit = '')
 329	{
 330		if ($limit != '' && is_numeric($limit))
 331		{
 332			$this->bcc_batch_mode = TRUE;
 333			$this->bcc_batch_size = $limit;
 334		}
 335
 336		$bcc = $this->_str_to_array($bcc);
 337		$bcc = $this->clean_email($bcc);
 338
 339		if ($this->validate)
 340		{
 341			$this->validate_email($bcc);
 342		}
 343
 344		if (($this->_get_protocol() == "smtp") OR ($this->bcc_batch_mode && count($bcc) > $this->bcc_batch_size))
 345		{
 346			$this->_bcc_array = $bcc;
 347		}
 348		else
 349		{
 350			$this->_set_header('Bcc', implode(", ", $bcc));
 351		}
 352
 353		return $this;
 354	}
 355
 356	// --------------------------------------------------------------------
 357
 358	/**
 359	 * Set Email Subject
 360	 *
 361	 * @access	public
 362	 * @param	string
 363	 * @return	void
 364	 */
 365	public function subject($subject)
 366	{
 367		$subject = $this->_prep_q_encoding($subject);
 368		$this->_set_header('Subject', $subject);
 369		return $this;
 370	}
 371
 372	// --------------------------------------------------------------------
 373
 374	/**
 375	 * Set Body
 376	 *
 377	 * @access	public
 378	 * @param	string
 379	 * @return	void
 380	 */
 381	public function message($body)
 382	{
 383		$this->_body = rtrim(str_replace("\r", "", $body));
 384
 385		/* strip slashes only if magic quotes is ON
 386		   if we do it with magic quotes OFF, it strips real, user-inputted chars.
 387
 388		   NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and
 389			 it will probably not exist in future versions at all.
 390		*/
 391		if ( ! is_php('5.4') && get_magic_quotes_gpc())
 392		{
 393			$this->_body = stripslashes($this->_body);
 394		}
 395
 396		return $this;
 397	}
 398
 399	// --------------------------------------------------------------------
 400
 401	/**
 402	 * Assign file attachments
 403	 *
 404	 * @access	public
 405	 * @param	string
 406	 * @return	void
 407	 */
 408	public function attach($filename, $disposition = 'attachment')
 409	{
 410		$this->_attach_name[] = $filename;
 411		$this->_attach_type[] = $this->_mime_types(pathinfo($filename, PATHINFO_EXTENSION));
 412		$this->_attach_disp[] = $disposition; // Can also be 'inline'  Not sure if it matters
 413		return $this;
 414	}
 415
 416	// --------------------------------------------------------------------
 417
 418	/**
 419	 * Add a Header Item
 420	 *
 421	 * @access	protected
 422	 * @param	string
 423	 * @param	string
 424	 * @return	void
 425	 */
 426	protected function _set_header($header, $value)
 427	{
 428		$this->_headers[$header] = $value;
 429	}
 430
 431	// --------------------------------------------------------------------
 432
 433	/**
 434	 * Convert a String to an Array
 435	 *
 436	 * @access	protected
 437	 * @param	string
 438	 * @return	array
 439	 */
 440	protected function _str_to_array($email)
 441	{
 442		if ( ! is_array($email))
 443		{
 444			if (strpos($email, ',') !== FALSE)
 445			{
 446				$email = preg_split('/[\s,]/', $email, -1, PREG_SPLIT_NO_EMPTY);
 447			}
 448			else
 449			{
 450				$email = trim($email);
 451				settype($email, "array");
 452			}
 453		}
 454		return $email;
 455	}
 456
 457	// --------------------------------------------------------------------
 458
 459	/**
 460	 * Set Multipart Value
 461	 *
 462	 * @access	public
 463	 * @param	string
 464	 * @return	void
 465	 */
 466	public function set_alt_message($str = '')
 467	{
 468		$this->alt_message = $str;
 469		return $this;
 470	}
 471
 472	// --------------------------------------------------------------------
 473
 474	/**
 475	 * Set Mailtype
 476	 *
 477	 * @access	public
 478	 * @param	string
 479	 * @return	void
 480	 */
 481	public function set_mailtype($type = 'text')
 482	{
 483		$this->mailtype = ($type == 'html') ? 'html' : 'text';
 484		return $this;
 485	}
 486
 487	// --------------------------------------------------------------------
 488
 489	/**
 490	 * Set Wordwrap
 491	 *
 492	 * @access	public
 493	 * @param	string
 494	 * @return	void
 495	 */
 496	public function set_wordwrap($wordwrap = TRUE)
 497	{
 498		$this->wordwrap = ($wordwrap === FALSE) ? FALSE : TRUE;
 499		return $this;
 500	}
 501
 502	// --------------------------------------------------------------------
 503
 504	/**
 505	 * Set Protocol
 506	 *
 507	 * @access	public
 508	 * @param	string
 509	 * @return	void
 510	 */
 511	public function set_protocol($protocol = 'mail')
 512	{
 513		$this->protocol = ( ! in_array($protocol, $this->_protocols, TRUE)) ? 'mail' : strtolower($protocol);
 514		return $this;
 515	}
 516
 517	// --------------------------------------------------------------------
 518
 519	/**
 520	 * Set Priority
 521	 *
 522	 * @access	public
 523	 * @param	integer
 524	 * @return	void
 525	 */
 526	public function set_priority($n = 3)
 527	{
 528		if ( ! is_numeric($n))
 529		{
 530			$this->priority = 3;
 531			return;
 532		}
 533
 534		if ($n < 1 OR $n > 5)
 535		{
 536			$this->priority = 3;
 537			return;
 538		}
 539
 540		$this->priority = $n;
 541		return $this;
 542	}
 543
 544	// --------------------------------------------------------------------
 545
 546	/**
 547	 * Set Newline Character
 548	 *
 549	 * @access	public
 550	 * @param	string
 551	 * @return	void
 552	 */
 553	public function set_newline($newline = "\n")
 554	{
 555		if ($newline != "\n" AND $newline != "\r\n" AND $newline != "\r")
 556		{
 557			$this->newline	= "\n";
 558			return;
 559		}
 560
 561		$this->newline	= $newline;
 562
 563		return $this;
 564	}
 565
 566	// --------------------------------------------------------------------
 567
 568	/**
 569	 * Set CRLF
 570	 *
 571	 * @access	public
 572	 * @param	string
 573	 * @return	void
 574	 */
 575	public function set_crlf($crlf = "\n")
 576	{
 577		if ($crlf != "\n" AND $crlf != "\r\n" AND $crlf != "\r")
 578		{
 579			$this->crlf	= "\n";
 580			return;
 581		}
 582
 583		$this->crlf	= $crlf;
 584
 585		return $this;
 586	}
 587
 588	// --------------------------------------------------------------------
 589
 590	/**
 591	 * Set Message Boundary
 592	 *
 593	 * @access	protected
 594	 * @return	void
 595	 */
 596	protected function _set_boundaries()
 597	{
 598		$this->_alt_boundary = "B_ALT_".uniqid(''); // multipart/alternative
 599		$this->_atc_boundary = "B_ATC_".uniqid(''); // attachment boundary
 600	}
 601
 602	// --------------------------------------------------------------------
 603
 604	/**
 605	 * Get the Message ID
 606	 *
 607	 * @access	protected
 608	 * @return	string
 609	 */
 610	protected function _get_message_id()
 611	{
 612		$from = $this->_headers['Return-Path'];
 613		$from = str_replace(">", "", $from);
 614		$from = str_replace("<", "", $from);
 615
 616		return  "<".uniqid('').strstr($from, '@').">";
 617	}
 618
 619	// --------------------------------------------------------------------
 620
 621	/**
 622	 * Get Mail Protocol
 623	 *
 624	 * @access	protected
 625	 * @param	bool
 626	 * @return	string
 627	 */
 628	protected function _get_protocol($return = TRUE)
 629	{
 630		$this->protocol = strtolower($this->protocol);
 631		$this->protocol = ( ! in_array($this->protocol, $this->_protocols, TRUE)) ? 'mail' : $this->protocol;
 632
 633		if ($return == TRUE)
 634		{
 635			return $this->protocol;
 636		}
 637	}
 638
 639	// --------------------------------------------------------------------
 640
 641	/**
 642	 * Get Mail Encoding
 643	 *
 644	 * @access	protected
 645	 * @param	bool
 646	 * @return	string
 647	 */
 648	protected function _get_encoding($return = TRUE)
 649	{
 650		$this->_encoding = ( ! in_array($this->_encoding, $this->_bit_depths)) ? '8bit' : $this->_encoding;
 651
 652		foreach ($this->_base_charsets as $charset)
 653		{
 654			if (strncmp($charset, $this->charset, strlen($charset)) == 0)
 655			{
 656				$this->_encoding = '7bit';
 657			}
 658		}
 659
 660		if ($return == TRUE)
 661		{
 662			return $this->_encoding;
 663		}
 664	}
 665
 666	// --------------------------------------------------------------------
 667
 668	/**
 669	 * Get content type (text/html/attachment)
 670	 *
 671	 * @access	protected
 672	 * @return	string
 673	 */
 674	protected function _get_content_type()
 675	{
 676		if	($this->mailtype == 'html' &&  count($this->_attach_name) == 0)
 677		{
 678			return 'html';
 679		}
 680		elseif	($this->mailtype == 'html' &&  count($this->_attach_name)  > 0)
 681		{
 682			return 'html-attach';
 683		}
 684		elseif	($this->mailtype == 'text' &&  count($this->_attach_name)  > 0)
 685		{
 686			return 'plain-attach';
 687		}
 688		else
 689		{
 690			return 'plain';
 691		}
 692	}
 693
 694	// --------------------------------------------------------------------
 695
 696	/**
 697	 * Set RFC 822 Date
 698	 *
 699	 * @access	protected
 700	 * @return	string
 701	 */
 702	protected function _set_date()
 703	{
 704		$timezone = date("Z");
 705		$operator = (strncmp($timezone, '-', 1) == 0) ? '-' : '+';
 706		$timezone = abs($timezone);
 707		$timezone = floor($timezone/3600) * 100 + ($timezone % 3600 ) / 60;
 708
 709		return sprintf("%s %s%04d", date("D, j M Y H:i:s"), $operator, $timezone);
 710	}
 711
 712	// --------------------------------------------------------------------
 713
 714	/**
 715	 * Mime message
 716	 *
 717	 * @access	protected
 718	 * @return	string
 719	 */
 720	protected function _get_mime_message()
 721	{
 722		return "This is a multi-part message in MIME format.".$this->newline."Your email application may not support this format.";
 723	}
 724
 725	// --------------------------------------------------------------------
 726
 727	/**
 728	 * Validate Email Address
 729	 *
 730	 * @access	public
 731	 * @param	string
 732	 * @return	bool
 733	 */
 734	public function validate_email($email)
 735	{
 736		if ( ! is_array($email))
 737		{
 738			$this->_set_error_message('lang:email_must_be_array');
 739			return FALSE;
 740		}
 741
 742		foreach ($email as $val)
 743		{
 744			if ( ! $this->valid_email($val))
 745			{
 746				$this->_set_error_message('lang:email_invalid_address', $val);
 747				return FALSE;
 748			}
 749		}
 750
 751		return TRUE;
 752	}
 753
 754	// --------------------------------------------------------------------
 755
 756	/**
 757	 * Email Validation
 758	 *
 759	 * @access	public
 760	 * @param	string
 761	 * @return	bool
 762	 */
 763	public function valid_email($address)
 764	{
 765		return ( ! preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $address)) ? FALSE : TRUE;
 766	}
 767
 768	// --------------------------------------------------------------------
 769
 770	/**
 771	 * Clean Extended Email Address: Joe Smith <joe@smith.com>
 772	 *
 773	 * @access	public
 774	 * @param	string
 775	 * @return	string
 776	 */
 777	public function clean_email($email)
 778	{
 779		if ( ! is_array($email))
 780		{
 781			if (preg_match('/\<(.*)\>/', $email, $match))
 782			{
 783				return $match['1'];
 784			}
 785			else
 786			{
 787				return $email;
 788			}
 789		}
 790
 791		$clean_email = array();
 792
 793		foreach ($email as $addy)
 794		{
 795			if (preg_match( '/\<(.*)\>/', $addy, $match))
 796			{
 797				$clean_email[] = $match['1'];
 798			}
 799			else
 800			{
 801				$clean_email[] = $addy;
 802			}
 803		}
 804
 805		return $clean_email;
 806	}
 807
 808	// --------------------------------------------------------------------
 809
 810	/**
 811	 * Build alternative plain text message
 812	 *
 813	 * This public function provides the raw message for use
 814	 * in plain-text headers of HTML-formatted emails.
 815	 * If the user hasn't specified his own alternative message
 816	 * it creates one by stripping the HTML
 817	 *
 818	 * @access	protected
 819	 * @return	string
 820	 */
 821	protected function _get_alt_message()
 822	{
 823		if ($this->alt_message != "")
 824		{
 825			return $this->word_wrap($this->alt_message, '76');
 826		}
 827
 828		if (preg_match('/\<body.*?\>(.*)\<\/body\>/si', $this->_body, $match))
 829		{
 830			$body = $match['1'];
 831		}
 832		else
 833		{
 834			$body = $this->_body;
 835		}
 836
 837		$body = trim(strip_tags($body));
 838		$body = preg_replace( '#<!--(.*)--\>#', "", $body);
 839		$body = str_replace("\t", "", $body);
 840
 841		for ($i = 20; $i >= 3; $i--)
 842		{
 843			$n = "";
 844
 845			for ($x = 1; $x <= $i; $x ++)
 846			{
 847				$n .= "\n";
 848			}
 849
 850			$body = str_replace($n, "\n\n", $body);
 851		}
 852
 853		return $this->word_wrap($body, '76');
 854	}
 855
 856	// --------------------------------------------------------------------
 857
 858	/**
 859	 * Word Wrap
 860	 *
 861	 * @access	public
 862	 * @param	string
 863	 * @param	integer
 864	 * @return	string
 865	 */
 866	public function word_wrap($str, $charlim = '')
 867	{
 868		// Se the character limit
 869		if ($charlim == '')
 870		{
 871			$charlim = ($this->wrapchars == "") ? "76" : $this->wrapchars;
 872		}
 873
 874		// Reduce multiple spaces
 875		$str = preg_replace("| +|", " ", $str);
 876
 877		// Standardize newlines
 878		if (strpos($str, "\r") !== FALSE)
 879		{
 880			$str = str_replace(array("\r\n", "\r"), "\n", $str);
 881		}
 882
 883		// If the current word is surrounded by {unwrap} tags we'll
 884		// strip the entire chunk and replace it with a marker.
 885		$unwrap = array();
 886		if (preg_match_all("|(\{unwrap\}.+?\{/unwrap\})|s", $str, $matches))
 887		{
 888			for ($i = 0; $i < count($matches['0']); $i++)
 889			{
 890				$unwrap[] = $matches['1'][$i];
 891				$str = str_replace($matches['1'][$i], "{{unwrapped".$i."}}", $str);
 892			}
 893		}
 894
 895		// Use PHP's native public function to do the initial wordwrap.
 896		// We set the cut flag to FALSE so that any individual words that are
 897		// too long get left alone.  In the next step we'll deal with them.
 898		$str = wordwrap($str, $charlim, "\n", FALSE);
 899
 900		// Split the string into individual lines of text and cycle through them
 901		$output = "";
 902		foreach (explode("\n", $str) as $line)
 903		{
 904			// Is the line within the allowed character count?
 905			// If so we'll join it to the output and continue
 906			if (strlen($line) <= $charlim)
 907			{
 908				$output .= $line.$this->newline;
 909				continue;
 910			}
 911
 912			$temp = '';
 913			while ((strlen($line)) > $charlim)
 914			{
 915				// If the over-length word is a URL we won't wrap it
 916				if (preg_match("!\[url.+\]|://|wwww.!", $line))
 917				{
 918					break;
 919				}
 920
 921				// Trim the word down
 922				$temp .= substr($line, 0, $charlim-1);
 923				$line = substr($line, $charlim-1);
 924			}
 925
 926			// If $temp contains data it means we had to split up an over-length
 927			// word into smaller chunks so we'll add it back to our current line
 928			if ($temp != '')
 929			{
 930				$output .= $temp.$this->newline.$line;
 931			}
 932			else
 933			{
 934				$output .= $line;
 935			}
 936
 937			$output .= $this->newline;
 938		}
 939
 940		// Put our markers back
 941		if (count($unwrap) > 0)
 942		{
 943			foreach ($unwrap as $key => $val)
 944			{
 945				$output = str_replace("{{unwrapped".$key."}}", $val, $output);
 946			}
 947		}
 948
 949		return $output;
 950	}
 951
 952	// --------------------------------------------------------------------
 953
 954	/**
 955	 * Build final headers
 956	 *
 957	 * @access	protected
 958	 * @param	string
 959	 * @return	string
 960	 */
 961	protected function _build_headers()
 962	{
 963		$this->_set_header('X-Sender', $this->clean_email($this->_headers['From']));
 964		$this->_set_header('X-Mailer', $this->useragent);
 965		$this->_set_header('X-Priority', $this->_priorities[$this->priority - 1]);
 966		$this->_set_header('Message-ID', $this->_get_message_id());
 967		$this->_set_header('Mime-Version', '1.0');
 968	}
 969
 970	// --------------------------------------------------------------------
 971
 972	/**
 973	 * Write Headers as a string
 974	 *
 975	 * @access	protected
 976	 * @return	void
 977	 */
 978	protected function _write_headers()
 979	{
 980		if ($this->protocol == 'mail')
 981		{
 982			$this->_subject = $this->_headers['Subject'];
 983			unset($this->_headers['Subject']);
 984		}
 985
 986		reset($this->_headers);
 987		$this->_header_str = "";
 988
 989		foreach ($this->_headers as $key => $val)
 990		{
 991			$val = trim($val);
 992
 993			if ($val != "")
 994			{
 995				$this->_header_str .= $key.": ".$val.$this->newline;
 996			}
 997		}
 998
 999		if ($this->_get_protocol() == 'mail')
1000		{
1001			$this->_header_str = rtrim($this->_header_str);
1002		}
1003	}
1004
1005	// --------------------------------------------------------------------
1006
1007	/**
1008	 * Build Final Body and attachments
1009	 *
1010	 * @access	protected
1011	 * @return	void
1012	 */
1013	protected function _build_message()
1014	{
1015		if ($this->wordwrap === TRUE  AND  $this->mailtype != 'html')
1016		{
1017			$this->_body = $this->word_wrap($this->_body);
1018		}
1019
1020		$this->_set_boundaries();
1021		$this->_write_headers();
1022
1023		$hdr = ($this->_get_protocol() == 'mail') ? $this->newline : '';
1024		$body = '';
1025
1026		switch ($this->_get_content_type())
1027		{
1028			case 'plain' :
1029
1030				$hdr .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1031				$hdr .= "Content-Transfer-Encoding: " . $this->_get_encoding();
1032
1033				if ($this->_get_protocol() == 'mail')
1034				{
1035					$this->_header_str .= $hdr;
1036					$this->_finalbody = $this->_body;
1037				}
1038				else
1039				{
1040					$this->_finalbody = $hdr . $this->newline . $this->newline . $this->_body;
1041				}
1042
1043				return;
1044
1045			break;
1046			case 'html' :
1047
1048				if ($this->send_multipart === FALSE)
1049				{
1050					$hdr .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
1051					$hdr .= "Content-Transfer-Encoding: quoted-printable";
1052				}
1053				else
1054				{
1055					$hdr .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline . $this->newline;
1056
1057					$body .= $this->_get_mime_message() . $this->newline . $this->newline;
1058					$body .= "--" . $this->_alt_boundary . $this->newline;
1059
1060					$body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1061					$body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
1062					$body .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
1063
1064					$body .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
1065					$body .= "Content-Transfer-Encoding: quoted-printable" . $this->newline . $this->newline;
1066				}
1067
1068				$this->_finalbody = $body . $this->_prep_quoted_printable($this->_body) . $this->newline . $this->newline;
1069
1070
1071				if ($this->_get_protocol() == 'mail')
1072				{
1073					$this->_header_str .= $hdr;
1074				}
1075				else
1076				{
1077					$this->_finalbody = $hdr . $this->_finalbody;
1078				}
1079
1080
1081				if ($this->send_multipart !== FALSE)
1082				{
1083					$this->_finalbody .= "--" . $this->_alt_boundary . "--";
1084				}
1085
1086				return;
1087
1088			break;
1089			case 'plain-attach' :
1090
1091				$hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline . $this->newline;
1092
1093				if ($this->_get_protocol() == 'mail')
1094				{
1095					$this->_header_str .= $hdr;
1096				}
1097
1098				$body .= $this->_get_mime_message() . $this->newline . $this->newline;
1099				$body .= "--" . $this->_atc_boundary . $this->newline;
1100
1101				$body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1102				$body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
1103
1104				$body .= $this->_body . $this->newline . $this->newline;
1105
1106			break;
1107			case 'html-attach' :
1108
1109				$hdr .= "Content-Type: multipart/".$this->multipart."; boundary=\"" . $this->_atc_boundary."\"" . $this->newline . $this->newline;
1110
1111				if ($this->_get_protocol() == 'mail')
1112				{
1113					$this->_header_str .= $hdr;
1114				}
1115
1116				$body .= $this->_get_mime_message() . $this->newline . $this->newline;
1117				$body .= "--" . $this->_atc_boundary . $this->newline;
1118
1119				$body .= "Content-Type: multipart/alternative; boundary=\"" . $this->_alt_boundary . "\"" . $this->newline .$this->newline;
1120				$body .= "--" . $this->_alt_boundary . $this->newline;
1121
1122				$body .= "Content-Type: text/plain; charset=" . $this->charset . $this->newline;
1123				$body .= "Content-Transfer-Encoding: " . $this->_get_encoding() . $this->newline . $this->newline;
1124				$body .= $this->_get_alt_message() . $this->newline . $this->newline . "--" . $this->_alt_boundary . $this->newline;
1125
1126				$body .= "Content-Type: text/html; charset=" . $this->charset . $this->newline;
1127				$body .= "Content-Transfer-Encoding: quoted-printable" . $this->newline . $this->newline;
1128
1129				$body .= $this->_prep_quoted_printable($this->_body) . $this->newline . $this->newline;
1130				$body .= "--" . $this->_alt_boundary . "--" . $this->newline . $this->newline;
1131
1132			break;
1133		}
1134
1135		$attachment = array();
1136
1137		$z = 0;
1138
1139		for ($i=0; $i < count($this->_attach_name); $i++)
1140		{
1141			$filename = $this->_attach_name[$i];
1142			$basename = basename($filename);
1143			$ctype = $this->_attach_type[$i];
1144
1145			if ( ! file_exists($filename))
1146			{
1147				$this->_set_error_message('lang:email_attachment_missing', $filename);
1148				return FALSE;
1149			}
1150
1151			$h  = "--".$this->_atc_boundary.$this->newline;
1152			$h .= "Content-type: ".$ctype."; ";
1153			$h .= "name=\"".$basename."\"".$this->newline;
1154			$h .= "Content-Disposition: ".$this->_attach_disp[$i].";".$this->newline;
1155			$h .= "Content-Transfer-Encoding: base64".$this->newline;
1156
1157			$attachment[$z++] = $h;
1158			$file = filesize($filename) +1;
1159
1160			if ( ! $fp = fopen($filename, FOPEN_READ))
1161			{
1162				$this->_set_error_message('lang:email_attachment_unreadable', $filename);
1163				return FALSE;
1164			}
1165
1166			$attachment[$z++] = chunk_split(base64_encode(fread($fp, $file)));
1167			fclose($fp);
1168		}
1169
1170		$body .= implode($this->newline, $attachment).$this->newline."--".$this->_atc_boundary."--";
1171
1172
1173		if ($this->_get_protocol() == 'mail')
1174		{
1175			$this->_finalbody = $body;
1176		}
1177		else
1178		{
1179			$this->_finalbody = $hdr . $body;
1180		}
1181
1182		return;
1183	}
1184
1185	// --------------------------------------------------------------------
1186
1187	/**
1188	 * Prep Quoted Printable
1189	 *
1190	 * Prepares string for Quoted-Printable Content-Transfer-Encoding
1191	 * Refer to RFC 2045 http://www.ietf.org/rfc/rfc2045.txt
1192	 *
1193	 * @access	protected
1194	 * @param	string
1195	 * @param	integer
1196	 * @return	string
1197	 */
1198	protected function _prep_quoted_printable($str, $charlim = '')
1199	{
1200		// Set the character limit
1201		// Don't allow over 76, as that will make servers and MUAs barf
1202		// all over quoted-printable data
1203		if ($charlim == '' OR $charlim > '76')
1204		{
1205			$charlim = '76';
1206		}
1207
1208		// Reduce multiple spaces
1209		$str = preg_replace("| +|", " ", $str);
1210
1211		// kill nulls
1212		$str = preg_replace('/\x00+/', '', $str);
1213
1214		// Standardize newlines
1215		if (strpos($str, "\r") !== FALSE)
1216		{
1217			$str = str_replace(array("\r\n", "\r"), "\n", $str);
1218		}
1219
1220		// We are intentionally wrapping so mail servers will encode characters
1221		// properly and MUAs will behave, so {unwrap} must go!
1222		$str = str_replace(array('{unwrap}', '{/unwrap}'), '', $str);
1223
1224		// Break into an array of lines
1225		$lines = explode("\n", $str);
1226
1227		$escape = '=';
1228		$output = '';
1229
1230		foreach ($lines as $line)
1231		{
1232			$length = strlen($line);
1233			$temp = '';
1234
1235			// Loop through each character in the line to add soft-wrap
1236			// characters at the end of a line " =\r\n" and add the newly
1237			// processed line(s) to the output (see comment on $crlf class property)
1238			for ($i = 0; $i < $length; $i++)
1239			{
1240				// Grab the next character
1241				$char = substr($line, $i, 1);
1242				$ascii = ord($char);
1243
1244				// Convert spaces and tabs but only if it's the end of the line
1245				if ($i == ($length - 1))
1246				{
1247					$char = ($ascii == '32' OR $ascii == '9') ? $escape.sprintf('%02s', dechex($ascii)) : $char;
1248				}
1249
1250				// encode = signs
1251				if ($ascii == '61')
1252				{
1253					$char = $escape.strtoupper(sprintf('%02s', dechex($ascii)));  // =3D
1254				}
1255
1256				// If we're at the character limit, add the line to the output,
1257				// reset our temp variable, and keep on chuggin'
1258				if ((strlen($temp) + strlen($char)) >= $charlim)
1259				{
1260					$output .= $temp.$escape.$this->crlf;
1261					$temp = '';
1262				}
1263
1264				// Add the character to our temporary line
1265				$temp .= $char;
1266			}
1267
1268			// Add our completed line to the output
1269			$output .= $temp.$this->crlf;
1270		}
1271
1272		// get rid of extra CRLF tacked onto the end
1273		$output = substr($output, 0, strlen($this->crlf) * -1);
1274
1275		return $output;
1276	}
1277
1278	// --------------------------------------------------------------------
1279
1280	/**
1281	 * Prep Q Encoding
1282	 *
1283	 * Performs "Q Encoding" on a string for use in email headers.  It's related
1284	 * but not identical to quoted-printable, so it has its own method
1285	 *
1286	 * @access	public
1287	 * @param	str
1288	 * @param	bool	// set to TRUE for processing From: headers
1289	 * @return	str
1290	 */
1291	protected function _prep_q_encoding($str, $from = FALSE)
1292	{
1293		$str = str_replace(array("\r", "\n"), array('', ''), $str);
1294
1295		// Line length must not exceed 76 characters, so we adjust for
1296		// a space, 7 extra characters =??Q??=, and the charset that we will add to each line
1297		$limit = 75 - 7 - strlen($this->charset);
1298
1299		// these special characters must be converted too
1300		$convert = array('_', '=', '?');
1301
1302		if ($from === TRUE)
1303		{
1304			$convert[] = ',';
1305			$convert[] = ';';
1306		}
1307
1308		$output = '';
1309		$temp = '';
1310
1311		for ($i = 0, $length = strlen($str); $i < $length; $i++)
1312		{
1313			// Grab the next character
1314			$char = substr($str, $i, 1);
1315			$ascii = ord($char);
1316
1317			// convert ALL non-printable ASCII characters and our specials
1318			if ($ascii < 32 OR $ascii > 126 OR in_array($char, $convert))
1319			{
1320				$char = '='.dechex($ascii);
1321			}
1322
1323			// handle regular spaces a bit more compactly than =20
1324			if ($ascii == 32)
1325			{
1326				$char = '_';
1327			}
1328
1329			// If we're at the character limit, add the line to the output,
1330			// reset our temp variable, and keep on chuggin'
1331			if ((strlen($temp) + strlen($char)) >= $limit)
1332			{
1333				$output .= $temp.$this->crlf;
1334				$temp = '';
1335			}
1336
1337			// Add the character to our temporary line
1338			$temp .= $char;
1339		}
1340
1341		$str = $output.$temp;
1342
1343		// wrap each line with the shebang, charset, and transfer encoding
1344		// the preceding space on successive lines is required for header "folding"
1345		$str = trim(preg_replace('/^(.*)$/m', ' =?'.$this->charset.'?Q?$1?=', $str));
1346
1347		return $str;
1348	}
1349
1350	// --------------------------------------------------------------------
1351
1352	/**
1353	 * Send Email
1354	 *
1355	 * @access	public
1356	 * @return	bool
1357	 */
1358	public function send()
1359	{
1360		if ($this->_replyto_flag == FALSE)
1361		{
1362			$this->reply_to($this->_headers['From']);
1363		}
1364
1365		if (( ! isset($this->_recipients) AND ! isset($this->_headers['To']))  AND
1366			( ! isset($this->_bcc_array) AND ! isset($this->_headers['Bcc'])) AND
1367			( ! isset($this->_headers['Cc'])))
1368		{
1369			$this->_set_error_message('lang:email_no_recipients');
1370			return FALSE;
1371		}
1372
1373		$this->_build_headers();
1374
1375		if ($this->bcc_batch_mode  AND  count($this->_bcc_array) > 0)
1376		{
1377			if (count($this->_bcc_array) > $this->bcc_batch_size)
1378				return $this->batch_bcc_send();
1379		}
1380
1381		$this->_build_message();
1382
1383		if ( ! $this->_spool_email())
1384		{
1385			return FALSE;
1386		}
1387		else
1388		{
1389			return TRUE;
1390		}
1391	}
1392
1393	// --------------------------------------------------------------------
1394
1395	/**
1396	 * Batch Bcc Send.  Sends groups of BCCs in batches
1397	 *
1398	 * @access	public
1399	 * @return	bool
1400	 */
1401	public function batch_bcc_send()
1402	{
1403		$float = $this->bcc_batch_size -1;
1404
1405		$set = "";
1406
1407		$chunk = array();
1408
1409		for ($i = 0; $i < count($this->_bcc_array); $i++)
1410		{
1411			if (isset($this->_bcc_array[$i]))
1412			{
1413				$set .= ", ".$this->_bcc_array[$i];
1414			}
1415
1416			if ($i == $float)
1417			{
1418				$chunk[] = substr($set, 1);
1419				$float = $float + $this->bcc_batch_size;
1420				$set = "";
1421			}
1422
1423			if ($i == count($this->_bcc_array)-1)
1424			{
1425				$chunk[] = substr($set, 1);
1426			}
1427		}
1428
1429		for ($i = 0; $i < count($chunk); $i++)
1430		{
1431			unset($this->_headers['Bcc']);
1432			unset($bcc);
1433
1434			$bcc = $this->_str_to_array($chunk[$i]);
1435			$bcc = $this->clean_email($bcc);
1436
1437			if ($this->protocol != 'smtp')
1438			{
1439				$this->_set_header('Bcc', implode(", ", $bcc));
1440			}
1441			else
1442			{
1443				$this->_bcc_array = $bcc;
1444			}
1445
1446			$this->_build_message();
1447			$this->_spool_email();
1448		}
1449	}
1450
1451	// --------------------------------------------------------------------
1452
1453	/**
1454	 * Unwrap special elements
1455	 *
1456	 * @access	protected
1457	 * @return	void
1458	 */
1459	protected function _unwrap_specials()
1460	{
1461		$this->_finalbody = preg_replace_callback("/\{unwrap\}(.*?)\{\/unwrap\}/si", array($this, '_remove_nl_callback'), $this->_finalbody);
1462	}
1463
1464	// --------------------------------------------------------------------
1465
1466	/**
1467	 * Strip line-breaks via callback
1468	 *
1469	 * @access	protected
1470	 * @return	string
1471	 */
1472	protected function _remove_nl_callback($matches)
1473	{
1474		if (strpos($matches[1], "\r") !== FALSE OR strpos($matches[1], "\n") !== FALSE)
1475		{
1476			$matches[1] = str_replace(array("\r\n", "\r", "\n"), '', $matches[1]);
1477		}
1478
1479		return $matches[1];
1480	}
1481
1482	// --------------------------------------------------------------------
1483
1484	/**
1485	 * Spool mail to the mail server
1486	 *
1487	 * @access	protected
1488	 * @return	bool
1489	 */
1490	protected function _spool_email()
1491	{
1492		$this->_unwrap_specials();
1493
1494		switch ($this->_get_protocol())
1495		{
1496			case 'mail'	:
1497
1498					if ( ! $this->_send_with_mail())
1499					{
1500						$this->_set_error_message('lang:email_send_failure_phpmail');
1501						return FALSE;
1502					}
1503			break;
1504			case 'sendmail'	:
1505
1506					if ( ! $this->_send_with_sendmail())
1507					{
1508						$this->_set_error_message('lang:email_send_failure_sendmail');
1509						return FALSE;
1510					}
1511			break;
1512			case 'smtp'	:
1513
1514					if ( ! $this->_send_with_smtp())
1515					{
1516						$this->_set_error_message('lang:email_send_failure_smtp');
1517						return FALSE;
1518					}
1519			break;
1520
1521		}
1522
1523		$this->_set_error_message('lang:email_sent', $this->_get_protocol());
1524		return TRUE;
1525	}
1526
1527	// --------------------------------------------------------------------
1528
1529	/**
1530	 * Send using mail()
1531	 *
1532	 * @access	protected
1533	 * @return	bool
1534	 */
1535	protected function _send_with_mail()
1536	{
1537		if ($this->_safe_mode == TRUE)
1538		{
1539			if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str))
1540			{
1541				return FALSE;
1542			}
1543			else
1544			{
1545				return TRUE;
1546			}
1547		}
1548		else
1549		{
1550			// most documentation of sendmail using the "-f" flag lacks a space after it, however
1551			// we've encountered servers that seem to require it to be in place.
1552
1553			if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, "-f ".$this->clean_email($this->_headers['From'])))
1554			{
1555				return FALSE;
1556			}
1557			else
1558			{
1559				return TRUE;
1560			}
1561		}
1562	}
1563
1564	// --------------------------------------------------------------------
1565
1566	/**
1567	 * Send using Sendmail
1568	 *
1569	 * @access	protected
1570	 * @return	bool
1571	 */
1572	protected function _send_with_sendmail()
1573	{
1574		$fp = @popen($this->mailpath . " -oi -f ".$this->clean_email($this->_headers['From'])." -t", 'w');
1575
1576		if ($fp === FALSE OR $fp === NULL)
1577		{
1578			// server probably has popen disabled, so nothing we can do to get a verbose error.
1579			return FALSE;
1580		}
1581
1582		fputs($fp, $this->_header_str);
1583		fputs($fp, $this->_finalbody);
1584
1585		$status = pclose($fp);
1586
1587		if (version_compare(PHP_VERSION, '4.2.3') == -1)
1588		{
1589			$status = $status >> 8 & 0xFF;
1590		}
1591
1592		if ($status != 0)
1593		{
1594			$this->_set_error_message('lang:email_exit_status', $status);
1595			$this->_set_error_message('lang:email_no_socket');
1596			return FALSE;
1597		}
1598
1599		return TRUE;
1600	}
1601
1602	// --------------------------------------------------------------------
1603
1604	/**
1605	 * Send using SMTP
1606	 *
1607	 * @access	protected
1608	 * @return	bool
1609	 */
1610	protected function _send_with_smtp()
1611	{
1612		if ($this->smtp_host == '')
1613		{
1614			$this->_set_error_message('lang:email_no_hostname');
1615			return FALSE;
1616		}
1617
1618		$this->_smtp_connect();
1619		$this->_smtp_authenticate();
1620
1621		$this->_send_command('from', $this->clean_email($this->_headers['From']));
1622
1623		foreach ($this->_recipients as $val)
1624		{
1625			$this->_send_command('to', $val);
1626		}
1627
1628		if (count($this->_cc_array) > 0)
1629		{
1630			foreach ($this->_cc_array as $val)
1631			{
1632				if ($val != "")
1633				{
1634					$this->_send_command('to', $val);
1635				}
1636			}
1637		}
1638
1639		if (count($this->_bcc_array) > 0)
1640		{
1641			foreach ($this->_bcc_array as $val)
1642			{
1643				if ($val != "")
1644				{
1645					$this->_send_command('to', $val);
1646				}
1647			}
1648		}
1649
1650		$this->_send_command('data');
1651
1652		// perform dot transformation on any lines that begin with a dot
1653		$this->_send_data($this->_header_str . preg_replace('/^\./m', '..$1', $this->_finalbody));
1654
1655		$this->_send_data('.');
1656
1657		$reply = $this->_get_smtp_data();
1658
1659		$this->_set_error_message($reply);
1660
1661		if (strncmp($reply, '250', 3) != 0)
1662		{
1663			$this->_set_error_message('lang:email_smtp_error', $reply);
1664			return FALSE;
1665		}
1666
1667		$this->_send_command('quit');
1668		return TRUE;
1669	}
1670
1671	// --------------------------------------------------------------------
1672
1673	/**
1674	 * SMTP Connect
1675	 *
1676	 * @access	protected
1677	 * @param	string
1678	 * @return	string
1679	 */
1680	protected function _smtp_connect()
1681	{
1682		$ssl = NULL;
1683		if ($this->smtp_crypto == 'ssl')
1684			$ssl = 'ssl://';
1685		$this->_smtp_connect = fsockopen($ssl.$this->smtp_host,
1686										$this->smtp_port,
1687										$errno,
1688										$errstr,
1689										$this->smtp_timeout);
1690
1691		if ( ! is_resource($this->_smtp_connect))
1692		{
1693			$this->_set_error_message('lang:email_smtp_error', $errno." ".$errstr);
1694			return FALSE;
1695		}
1696
1697		$this->_set_error_message($this->_get_smtp_data());
1698
1699		if ($this->smtp_crypto == 'tls')
1700		{
1701			$this->_send_command('hello');
1702			$this->_send_command('starttls');
1703			stream_socket_enable_crypto($this->_smtp_connect, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT);
1704		}
1705
1706		return $this->_send_command('hello');
1707	}
1708
1709	// --------------------------------------------------------------------
1710
1711	/**
1712	 * Send SMTP command
1713	 *
1714	 * @access	protected
1715	 * @param	string
1716	 * @param	string
1717	 * @return	string
1718	 */
1719	protected function _send_command($cmd, $data = '')
1720	{
1721		switch ($cmd)
1722		{
1723			case 'hello' :
1724
1725					if ($this->_smtp_auth OR $this->_get_encoding() == '8bit')
1726						$this->_send_data('EHLO '.$this->_get_hostname());
1727					else
1728						$this->_send_data('HELO '.$this->_get_hostname());
1729
1730						$resp = 250;
1731			break;
1732			case 'starttls'	:
1733
1734						$this->_send_data('STARTTLS');
1735
1736						$resp = 220;
1737			break;
1738			case 'from' :
1739
1740						$this->_send_data('MAIL FROM:<'.$data.'>');
1741
1742						$resp = 250;
1743			break;
1744			case 'to'	:
1745
1746						$this->_send_data('RCPT TO:<'.$data.'>');
1747
1748						$resp = 250;
1749			break;
1750			case 'data'	:
1751
1752						$this->_send_data('DATA');
1753
1754						$resp = 354;
1755			break;
1756			case 'quit'	:
1757
1758						$this->_send_data('QUIT');
1759
1760						$resp = 221;
1761			break;
1762		}
1763
1764		$reply = $this->_get_smtp_data();
1765
1766		$this->_debug_msg[] = "<pre>".$cmd.": ".$reply."</pre>";
1767
1768		if (substr($reply, 0, 3) != $resp)
1769		{
1770			$this->_set_error_message('lang:email_smtp_error', $reply);
1771			return FALSE;
1772		}
1773
1774		if ($cmd == 'quit')
1775		{
1776			fclose($this->_smtp_connect);
1777		}
1778
1779		return TRUE;
1780	}
1781
1782	// --------------------------------------------------------------------
1783
1784	/**
1785	 *  SMTP Authenticate
1786	 *
1787	 * @access	protected
1788	 * @return	bool
1789	 */
1790	protected function _smtp_authenticate()
1791	{
1792		if ( ! $this->_smtp_auth)
1793		{
1794			return TRUE;
1795		}
1796
1797		if ($this->smtp_user == ""  AND  $this->smtp_pass == "")
1798		{
1799			$this->_set_error_message('lang:email_no_smtp_unpw');
1800			return FALSE;
1801		}
1802
1803		$this->_send_data('AUTH LOGIN');
1804
1805		$reply = $this->_get_smtp_data();
1806
1807		if (strncmp($reply, '334', 3) != 0)
1808		{
1809			$this->_set_error_message('lang:email_failed_smtp_login', $reply);
1810			return FALSE;
1811		}
1812
1813		$this->_send_data(base64_encode($this->smtp_user));
1814
1815		$reply = $this->_get_smtp_data();
1816
1817		if (strncmp($reply, '334', 3) != 0)
1818		{
1819			$this->_set_error_message('lang:email_smtp_auth_un', $reply);
1820			return FALSE;
1821		}
1822
1823		$this->_send_data(base64_encode($this->smtp_pass));
1824
1825		$reply = $this->_get_smtp_data();
1826
1827		if (strncmp($reply, '235', 3) != 0)
1828		{
1829			$this->_set_error_message('lang:email_smtp_auth_pw', $reply);
1830			return FALSE;
1831		}
1832
1833		return TRUE;
1834	}
1835
1836	// --------------------------------------------------------------------
1837
1838	/**
1839	 * Send SMTP data
1840	 *
1841	 * @access	protected
1842	 * @return	bool
1843	 */
1844	protected function _send_data($data)
1845	{
1846		if ( ! fwrite($this->_smtp_connect, $data . $this->newline))
1847		{
1848			$this->_set_error_message('lang:email_smtp_data_failure', $data);
1849			return FALSE;
1850		}
1851		else
1852		{
1853			return TRUE;
1854		}
1855	}
1856
1857	// --------------------------------------------------------------------
1858
1859	/**
1860	 * Get SMTP data
1861	 *
1862	 * @access	protected
1863	 * @return	string
1864	 */
1865	protected function _get_smtp_data()
1866	{
1867		$data = "";
1868
1869		while ($str = fgets($this->_smtp_connect, 512))
1870		{
1871			$data .= $str;
1872
1873			if (substr($str, 3, 1) == " ")
1874			{
1875				break;
1876			}
1877		}
1878
1879		return $data;
1880	}
1881
1882	// --------------------------------------------------------------------
1883
1884	/**
1885	 * Get Hostname
1886	 *
1887	 * @access	protected
1888	 * @return	string
1889	 */
1890	protected function _get_hostname()
1891	{
1892		return (isset($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : 'localhost.localdomain';
1893	}
1894
1895	// --------------------------------------------------------------------
1896
1897	/**
1898	 * Get IP
1899	 *
1900	 * @access	protected
1901	 * @return	string
1902	 */
1903	protected function _get_ip()
1904	{
1905		if ($this->_IP !== FALSE)
1906		{
1907			return $this->_IP;
1908		}
1909
1910		$cip = (isset($_SERVER['HTTP_CLIENT_IP']) AND $_SERVER['HTTP_CLIENT_IP'] != "") ? $_SERVER['HTTP_CLIENT_IP'] : FALSE;
1911		$rip = (isset($_SERVER['REMOTE_ADDR']) AND $_SERVER['REMOTE_ADDR'] != "") ? $_SERVER['REMOTE_ADDR'] : FALSE;
1912		$fip = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND $_SERVER['HTTP_X_FORWARDED_FOR'] != "") ? $_SERVER['HTTP_X_FORWARDED_FOR'] : FALSE;
1913
1914		if ($cip && $rip)	$this->_IP = $cip;
1915		elseif ($rip)		$this->_IP = $rip;
1916		elseif ($cip)		$this->_IP = $cip;
1917		elseif ($fip)		$this->_IP = $fip;
1918
1919		if (strpos($this->_IP, ',') !== FALSE)
1920		{
1921			$x = explode(',', $this->_IP);
1922			$this->_IP = end($x);
1923		}
1924
1925		if ( ! preg_match( "/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $this->_IP))
1926		{
1927			$this->_IP = '0.0.0.0';
1928		}
1929
1930		unset($cip);
1931		unset($rip);
1932		unset($fip);
1933
1934		return $this->_IP;
1935	}
1936
1937	// --------------------------------------------------------------------
1938
1939	/**
1940	 * Get Debug Message
1941	 *
1942	 * @access	public
1943	 * @return	string
1944	 */
1945	public function print_debugger()
1946	{
1947		$msg = '';
1948
1949		if (count($this->_debug_msg) > 0)
1950		{
1951			foreach ($this->_debug_msg as $val)
1952			{
1953				$msg .= $val;
1954			}
1955		}
1956
1957		$msg .= "<pre>".$this->_header_str."\n".htmlspecialchars($this->_subject)."\n".htmlspecialchars($this->_finalbody).'</pre>';
1958		return $msg;
1959	}
1960
1961	// --------------------------------------------------------------------
1962
1963	/**
1964	 * Set Message
1965	 *
1966	 * @access	protected
1967	 * @param	string
1968	 * @return	string
1969	 */
1970	protected function _set_error_message($msg, $val = '')
1971	{
1972		$CI =& get_instance();
1973		$CI->lang->load('email');
1974
1975		if (substr($msg, 0, 5) != 'lang:' || FALSE === ($line = $CI->lang->line(substr($msg, 5))))
1976		{
1977			$this->_debug_msg[] = str_replace('%s', $val, $msg)."<br />";
1978		}
1979		else
1980		{
1981			$this->_debug_msg[] = str_replace('%s', $val, $line)."<br />";
1982		}
1983	}
1984
1985	// --------------------------------------------------------------------
1986
1987	/**
1988	 * Mime Types
1989	 *
1990	 * @access	protected
1991	 * @param	string
1992	 * @return	string
1993	 */
1994	protected function _mime_types($ext = "")
1995	{
1996		$mimes = array(	'hqx'	=>	'application/mac-binhex40',
1997						'cpt'	=>	'application/mac-compactpro',
1998						'doc'	=>	'application/msword',
1999						'bin'	=>	'application/macbinary',
2000						'dms'	=>	'application/octet-stream',
2001						'lha'	=>	'application/octet-stream',
2002						'lzh'	=>	'application/octet-stream',
2003						'exe'	=>	'application/octet-stream',
2004						'class'	=>	'application/octet-stream',
2005						'psd'	=>	'application/octet-stream',
2006						'so'	=>	'application/octet-stream',
2007						'sea'	=>	'application/octet-stream',
2008						'dll'	=>	'application/octet-stream',
2009						'oda'	=>	'application/oda',
2010						'pdf'	=>	'application/pdf',
2011						'ai'	=>	'application/postscript',
2012						'eps'	=>	'application/postscript',
2013						'ps'	=>	'application/postscript',
2014						'smi'	=>	'application/smil',
2015						'smil'	=>	'application/smil',
2016						'mif'	=>	'application/vnd.mif',
2017						'xls'	=>	'application/vnd.ms-excel',
2018						'ppt'	=>	'application/vnd.ms-powerpoint',
2019						'wbxml'	=>	'application/vnd.wap.wbxml',
2020						'wmlc'	=>	'application/vnd.wap.wmlc',
2021						'dcr'	=>	'application/x-director',
2022						'dir'	=>	'application/x-director',
2023						'dxr'	=>	'application/x-director',
2024						'dvi'	=>	'application/x-dvi',
2025						'gtar'	=>	'application/x-gtar',
2026						'php'	=>	'application/x-httpd-php',
2027						'php4'	=>	'application/x-httpd-php',
2028						'php3'	=>	'application/x-httpd-php',
2029						'phtml'	=>	'application/x-httpd-php',
2030						'phps'	=>	'application/x-httpd-php-source',
2031						'js'	=>	'application/x-javascript',
2032						'swf'	=>	'application/x-shockwave-flash',
2033						'sit'	=>	'application/x-stuffit',
2034						'tar'	=>	'application/x-tar',
2035						'tgz'	=>	'application/x-tar',
2036						'xhtml'	=>	'application/xhtml+xml',
2037						'xht'	=>	'application/xhtml+xml',
2038						'zip'	=>	'application/zip',
2039						'mid'	=>	'audio/midi',
2040						'midi'	=>	'audio/midi',
2041						'mpga'	=>	'audio/mpeg',
2042						'mp2'	=>	'audio/mpeg',
2043						'mp3'	=>	'audio/mpeg',
2044						'aif'	=>	'audio/x-aiff',
2045						'aiff'	=>	'audio/x-aiff',
2046						'aifc'	=>	'audio/x-aiff',
2047						'ram'	=>	'audio/x-pn-realaudio',
2048						'rm'	=>	'audio/x-pn-realaudio',
2049						'rpm'	=>	'audio/x-pn-realaudio-plugin',
2050						'ra'	=>	'audio/x-realaudio',
2051						'rv'	=>	'video/vnd.rn-realvideo',
2052						'wav'	=>	'audio/x-wav',
2053						'bmp'	=>	'image/bmp',
2054						'gif'	=>	'image/gif',
2055						'jpeg'	=>	'image/jpeg',
2056						'jpg'	=>	'image/jpeg',
2057						'jpe'	=>	'image/jpeg',
2058						'png'	=>	'image/png',
2059						'tiff'	=>	'image/tiff',
2060						'tif'	=>	'image/tiff',
2061						'css'	=>	'text/css',
2062						'html'	=>	'text/html',
2063						'htm'	=>	'text/html',
2064						'shtml'	=>	'text/html',
2065						'txt'	=>	'text/plain',
2066						'text'	=>	'text/plain',
2067						'log'	=>	'text/plain',
2068						'rtx'	=>	'text/richtext',
2069						'rtf'	=>	'text/rtf',
2070						'xml'	=>	'text/xml',
2071						'xsl'	=>	'text/xml',
2072						'mpeg'	=>	'video/mpeg',
2073						'mpg'	=>	'video/mpeg',
2074						'mpe'	=>	'video/mpeg',
2075						'qt'	=>	'video/quicktime',
2076						'mov'	=>	'video/quicktime',
2077						'avi'	=>	'video/x-msvideo',
2078						'movie'	=>	'video/x-sgi-movie',
2079						'doc'	=>	'application/msword',
2080						'word'	=>	'application/msword',
2081						'xl'	=>	'application/excel',
2082						'eml'	=>	'message/rfc822'
2083					);
2084
2085		return ( ! isset($mimes[strtolower($ext)])) ? "application/x-unknown-content-type" : $mimes[strtolower($ext)];
2086	}
2087
2088}
2089// END CI_Email class
2090
2091/* End of file Email.php */
2092/* Location: ./system/libraries/Email.php */