· 9 years ago · Oct 28, 2016, 04:18 PM
1<?php
2/**
3 * PHPMailer RFC821 SMTP email transport class.
4 * PHP Version 5
5 * @package PHPMailer
6 * @link https://paypal.com
7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
10 * @author Brent R. Matzelle (original founder)
11 * @copyright 2014 Marcus Bointon
12 * @copyright 2010 - 2012 Jim Jagielski
13 * @copyright 2004 - 2009 Andy Prevost
14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15 * @note This program is distributed in the hope that it will be useful - WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE.
18 */
19
20/**
21 * PHPMailer RFC821 SMTP email transport class.
22 * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
23 * @package PHPMailer
24 * @author Chris Ryan
25 * @author Marcus Bointon <phpmailer@synchromedia.co.uk>
26 */
27class SMTP
28{
29 /**
30 * The PHPMailer SMTP version number.
31 * @type string
32 */
33 const VERSION = '5.2.11';
34
35 /**
36 * SMTP line break constant.
37 * @type string
38 */
39 const CRLF = "\r\n";
40
41 /**
42 * The SMTP port to use if one is not specified.
43 * @type integer
44 */
45 const DEFAULT_SMTP_PORT = 25;
46
47 /**
48 * The maximum line length allowed by RFC 2822 section 2.1.1
49 * @type integer
50 */
51 const MAX_LINE_LENGTH = 998;
52
53 /**
54 * Debug level for no output
55 */
56 const DEBUG_OFF = 0;
57
58 /**
59 * Debug level to show client -> server messages
60 */
61 const DEBUG_CLIENT = 1;
62
63 /**
64 * Debug level to show client -> server and server -> client messages
65 */
66 const DEBUG_SERVER = 2;
67
68 /**
69 * Debug level to show connection status, client -> server and server -> client messages
70 */
71 const DEBUG_CONNECTION = 3;
72
73 /**
74 * Debug level to show all messages
75 */
76 const DEBUG_LOWLEVEL = 4;
77
78 /**
79 * The PHPMailer SMTP Version number.
80 * @type string
81 * @deprecated Use the `VERSION` constant instead
82 * @see SMTP::VERSION
83 */
84 public $Version = '5.2.11';
85
86 /**
87 * SMTP server port number.
88 * @type integer
89 * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead
90 * @see SMTP::DEFAULT_SMTP_PORT
91 */
92 public $SMTP_PORT = 25;
93
94 /**
95 * SMTP reply line ending.
96 * @type string
97 * @deprecated Use the `CRLF` constant instead
98 * @see SMTP::CRLF
99 */
100 public $CRLF = "\r\n";
101
102 /**
103 * Debug output level.
104 * Options:
105 * * self::DEBUG_OFF (`0`) No debug output, default
106 * * self::DEBUG_CLIENT (`1`) Client commands
107 * * self::DEBUG_SERVER (`2`) Client commands and server responses
108 * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
109 * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages
110 * @type integer
111 */
112 public $do_debug = self::DEBUG_OFF;
113
114 /**
115 * How to handle debug output.
116 * Options:
117 * * `echo` Output plain-text as-is, appropriate for CLI
118 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
119 * * `error_log` Output to error log as configured in php.ini
120 *
121 * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
122 * <code>
123 * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
124 * </code>
125 * @type string|callable
126 */
127 public $Debugoutput = 'echo';
128
129 /**
130 * Whether to use VERP.
131 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
132 * @link http://www.postfix.org/VERP_README.html Info on VERP
133 * @type boolean
134 */
135 public $do_verp = false;
136
137 /**
138 * The timeout value for connection, in seconds.
139 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
140 * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
141 * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
142 * @type integer
143 */
144 public $Timeout = 300;
145
146 /**
147 * How long to wait for commands to complete, in seconds.
148 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
149 * @type integer
150 */
151 public $Timelimit = 300;
152
153 /**
154 * The socket for the server connection.
155 * @type resource
156 */
157 protected $smtp_conn;
158
159 /**
160 * Error information, if any, for the last SMTP command.
161 * @type array
162 */
163 protected $error = array(
164 'error' => '',
165 'detail' => '',
166 'smtp_code' => '',
167 'smtp_code_ex' => ''
168 );
169
170 /**
171 * The reply the server sent to us for HELO.
172 * If null, no HELO string has yet been received.
173 * @type string|null
174 */
175 protected $helo_rply = null;
176
177 /**
178 * The set of SMTP extensions sent in reply to EHLO command.
179 * Indexes of the array are extension names.
180 * Value at index 'HELO' or 'EHLO' (according to command that was sent)
181 * represents the server name. In case of HELO it is the only element of the array.
182 * Other values can be boolean TRUE or an array containing extension options.
183 * If null, no HELO/EHLO string has yet been received.
184 * @type array|null
185 */
186 protected $server_caps = null;
187
188 /**
189 * The most recent reply received from the server.
190 * @type string
191 */
192 protected $last_reply = '';
193
194 /**
195 * Output debugging info via a user-selected method.
196 * @see SMTP::$Debugoutput
197 * @see SMTP::$do_debug
198 * @param string $str Debug string to output
199 * @param integer $level The debug level of this message; see DEBUG_* constants
200 * @return void
201 */
202 protected function edebug($str, $level = 0)
203 {
204 if ($level > $this->do_debug) {
205 return;
206 }
207 //Avoid clash with built-in function names
208 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
209 call_user_func($this->Debugoutput, $str, $this->do_debug);
210 return;
211 }
212 switch ($this->Debugoutput) {
213 case 'error_log':
214 //Don't output, just log
215 error_log($str);
216 break;
217 case 'html':
218 //Cleans up output a bit for a better looking, HTML-safe output
219 echo htmlentities(
220 preg_replace('/[\r\n]+/', '', $str),
221 ENT_QUOTES,
222 'UTF-8'
223 )
224 . "<br>\n";
225 break;
226 case 'echo':
227 default:
228 //Normalize line breaks
229 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
230 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
231 "\n",
232 "\n \t ",
233 trim($str)
234 )."\n";
235 }
236 }
237
238 /**
239 * Connect to an SMTP server.
240 * @param string $host SMTP server IP or host name
241 * @param integer $port The port number to connect to
242 * @param integer $timeout How long to wait for the connection to open
243 * @param array $options An array of options for stream_context_create()
244 * @access public
245 * @return boolean
246 */
247 public function connect($host, $port = null, $timeout = 30, $options = array())
248 {
249 static $streamok;
250 //This is enabled by default since 5.0.0 but some providers disable it
251 //Check this once and cache the result
252 if (is_null($streamok)) {
253 $streamok = function_exists('stream_socket_client');
254 }
255 // Clear errors to avoid confusion
256 $this->setError('');
257 // Make sure we are __not__ connected
258 if ($this->connected()) {
259 // Already connected, generate error
260 $this->setError('Already connected to a server');
261 return false;
262 }
263 if (empty($port)) {
264 $port = self::DEFAULT_SMTP_PORT;
265 }
266 // Connect to the SMTP server
267 $this->edebug(
268 "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true),
269 self::DEBUG_CONNECTION
270 );
271 $errno = 0;
272 $errstr = '';
273 if ($streamok) {
274 $socket_context = stream_context_create($options);
275 //Suppress errors; connection failures are handled at a higher level
276 $this->smtp_conn = @stream_socket_client(
277 $host . ":" . $port,
278 $errno,
279 $errstr,
280 $timeout,
281 STREAM_CLIENT_CONNECT,
282 $socket_context
283 );
284 } else {
285 //Fall back to fsockopen which should work in more places, but is missing some features
286 $this->edebug(
287 "Connection: stream_socket_client not available, falling back to fsockopen",
288 self::DEBUG_CONNECTION
289 );
290 $this->smtp_conn = fsockopen(
291 $host,
292 $port,
293 $errno,
294 $errstr,
295 $timeout
296 );
297 }
298 // Verify we connected properly
299 if (!is_resource($this->smtp_conn)) {
300 $this->setError(
301 'Failed to connect to server',
302 $errno,
303 $errstr
304 );
305 $this->edebug(
306 'SMTP ERROR: ' . $this->error['error']
307 . ": $errstr ($errno)",
308 self::DEBUG_CLIENT
309 );
310 return false;
311 }
312 $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
313 // SMTP server can take longer to respond, give longer timeout for first read
314 // Windows does not have support for this timeout function
315 if (substr(PHP_OS, 0, 3) != 'WIN') {
316 $max = ini_get('max_execution_time');
317 // Don't bother if unlimited
318 if ($max != 0 && $timeout > $max) {
319 @set_time_limit($timeout);
320 }
321 stream_set_timeout($this->smtp_conn, $timeout, 0);
322 }
323 // Get any announcement
324 $announce = $this->get_lines();
325 $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
326 return true;
327 }
328
329 /**
330 * Initiate a TLS (encrypted) session.
331 * @access public
332 * @return boolean
333 */
334 public function startTLS()
335 {
336 if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
337 return false;
338 }
339 // Begin encrypted connection
340 if (!stream_socket_enable_crypto(
341 $this->smtp_conn,
342 true,
343 STREAM_CRYPTO_METHOD_TLS_CLIENT
344 )) {
345 return false;
346 }
347 return true;
348 }
349
350 /**
351 * Perform SMTP authentication.
352 * Must be run after hello().
353 * @see hello()
354 * @param string $username The user name
355 * @param string $password The password
356 * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5)
357 * @param string $realm The auth realm for NTLM
358 * @param string $workstation The auth workstation for NTLM
359 * @access public
360 * @return boolean True if successfully authenticated.
361 */
362 public function authenticate(
363 $username,
364 $password,
365 $authtype = null,
366 $realm = '',
367 $workstation = ''
368 ) {
369 if (!$this->server_caps) {
370 $this->setError('Authentication is not allowed before HELO/EHLO');
371 return false;
372 }
373
374 if (array_key_exists('EHLO', $this->server_caps)) {
375 // SMTP extensions are available. Let's try to find a proper authentication method
376
377 if (!array_key_exists('AUTH', $this->server_caps)) {
378 $this->setError('Authentication is not allowed at this stage');
379 // 'at this stage' means that auth may be allowed after the stage changes
380 // e.g. after STARTTLS
381 return false;
382 }
383
384 self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);
385 self::edebug(
386 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
387 self::DEBUG_LOWLEVEL
388 );
389
390 if (empty($authtype)) {
391 foreach (array('LOGIN', 'CRAM-MD5', 'NTLM', 'PLAIN') as $method) {
392 if (in_array($method, $this->server_caps['AUTH'])) {
393 $authtype = $method;
394 break;
395 }
396 }
397 if (empty($authtype)) {
398 $this->setError('No supported authentication methods found');
399 return false;
400 }
401 self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL);
402 }
403
404 if (!in_array($authtype, $this->server_caps['AUTH'])) {
405 $this->setError("The requested authentication method \"$authtype\" is not supported by the server");
406 return false;
407 }
408 } elseif (empty($authtype)) {
409 $authtype = 'LOGIN';
410 }
411 switch ($authtype) {
412 case 'PLAIN':
413 // Start authentication
414 if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
415 return false;
416 }
417 // Send encoded username and password
418 if (!$this->sendCommand(
419 'User & Password',
420 base64_encode("\0" . $username . "\0" . $password),
421 235
422 )
423 ) {
424 return false;
425 }
426 break;
427 case 'LOGIN':
428 // Start authentication
429 if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
430 return false;
431 }
432 if (!$this->sendCommand("Username", base64_encode($username), 334)) {
433 return false;
434 }
435 if (!$this->sendCommand("Password", base64_encode($password), 235)) {
436 return false;
437 }
438 break;
439 case 'NTLM':
440 /*
441 * ntlm_sasl_client.php
442 * Bundled with Permission
443 *
444 * How to telnet in windows:
445 * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
446 * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
447 */
448 require_once 'extras/ntlm_sasl_client.php';
449 $temp = new stdClass;
450 $ntlm_client = new ntlm_sasl_client_class;
451 //Check that functions are available
452 if (!$ntlm_client->Initialize($temp)) {
453 $this->setError($temp->error);
454 $this->edebug(
455 'You need to enable some modules in your php.ini file: '
456 . $this->error['error'],
457 self::DEBUG_CLIENT
458 );
459 return false;
460 }
461 //msg1
462 $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1
463
464 if (!$this->sendCommand(
465 'AUTH NTLM',
466 'AUTH NTLM ' . base64_encode($msg1),
467 334
468 )
469 ) {
470 return false;
471 }
472 //Though 0 based, there is a white space after the 3 digit number
473 //msg2
474 $challenge = substr($this->last_reply, 3);
475 $challenge = base64_decode($challenge);
476 $ntlm_res = $ntlm_client->NTLMResponse(
477 substr($challenge, 24, 8),
478 $password
479 );
480 //msg3
481 $msg3 = $ntlm_client->TypeMsg3(
482 $ntlm_res,
483 $username,
484 $realm,
485 $workstation
486 );
487 // send encoded username
488 return $this->sendCommand('Username', base64_encode($msg3), 235);
489 case 'CRAM-MD5':
490 // Start authentication
491 if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
492 return false;
493 }
494 // Get the challenge
495 $challenge = base64_decode(substr($this->last_reply, 4));
496
497 // Build the response
498 $response = $username . ' ' . $this->hmac($challenge, $password);
499
500 // send encoded credentials
501 return $this->sendCommand('Username', base64_encode($response), 235);
502 default:
503 $this->setError("Authentication method \"$authtype\" is not supported");
504 return false;
505 }
506 return true;
507 }
508
509 /**
510 * Calculate an MD5 HMAC hash.
511 * Works like hash_hmac('md5', $data, $key)
512 * in case that function is not available
513 * @param string $data The data to hash
514 * @param string $key The key to hash with
515 * @access protected
516 * @return string
517 */
518 protected function hmac($data, $key)
519 {
520 if (function_exists('hash_hmac')) {
521 return hash_hmac('md5', $data, $key);
522 }
523
524 // The following borrowed from
525 // http://php.net/manual/en/function.mhash.php#27225
526
527 // RFC 2104 HMAC implementation for php.
528 // Creates an md5 HMAC.
529 // Eliminates the need to install mhash to compute a HMAC
530 // by Lance Rushing
531
532 $bytelen = 64; // byte length for md5
533 if (strlen($key) > $bytelen) {
534 $key = pack('H*', md5($key));
535 }
536 $key = str_pad($key, $bytelen, chr(0x00));
537 $ipad = str_pad('', $bytelen, chr(0x36));
538 $opad = str_pad('', $bytelen, chr(0x5c));
539 $k_ipad = $key ^ $ipad;
540 $k_opad = $key ^ $opad;
541
542 return md5($k_opad . pack('H*', md5($k_ipad . $data)));
543 }
544
545 /**
546 * Check connection state.
547 * @access public
548 * @return boolean True if connected.
549 */
550 public function connected()
551 {
552 if (is_resource($this->smtp_conn)) {
553 $sock_status = stream_get_meta_data($this->smtp_conn);
554 if ($sock_status['eof']) {
555 // The socket is valid but we are not connected
556 $this->edebug(
557 'SMTP NOTICE: EOF caught while checking if connected',
558 self::DEBUG_CLIENT
559 );
560 $this->close();
561 return false;
562 }
563 return true; // everything looks good
564 }
565 return false;
566 }
567
568 /**
569 * Close the socket and clean up the state of the class.
570 * Don't use this function without first trying to use QUIT.
571 * @see quit()
572 * @access public
573 * @return void
574 */
575 public function close()
576 {
577 $this->setError('');
578 $this->server_caps = null;
579 $this->helo_rply = null;
580 if (is_resource($this->smtp_conn)) {
581 // close the connection and cleanup
582 fclose($this->smtp_conn);
583 $this->smtp_conn = null; //Makes for cleaner serialization
584 $this->edebug('Connection: closed', self::DEBUG_CONNECTION);
585 }
586 }
587
588 /**
589 * Send an SMTP DATA command.
590 * Issues a data command and sends the msg_data to the server,
591 * finializing the mail transaction. $msg_data is the message
592 * that is to be send with the headers. Each header needs to be
593 * on a single line followed by a <CRLF> with the message headers
594 * and the message body being separated by and additional <CRLF>.
595 * Implements rfc 821: DATA <CRLF>
596 * @param string $msg_data Message data to send
597 * @access public
598 * @return boolean
599 */
600 public function data($msg_data)
601 {
602 //This will use the standard timelimit
603 if (!$this->sendCommand('DATA', 'DATA', 354)) {
604 return false;
605 }
606
607 /* The server is ready to accept data!
608 * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
609 * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
610 * smaller lines to fit within the limit.
611 * We will also look for lines that start with a '.' and prepend an additional '.'.
612 * NOTE: this does not count towards line-length limit.
613 */
614
615 // Normalize line breaks before exploding
616 $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
617
618 /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
619 * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
620 * process all lines before a blank line as headers.
621 */
622
623 $field = substr($lines[0], 0, strpos($lines[0], ':'));
624 $in_headers = false;
625 if (!empty($field) && strpos($field, ' ') === false) {
626 $in_headers = true;
627 }
628
629 foreach ($lines as $line) {
630 $lines_out = array();
631 if ($in_headers and $line == '') {
632 $in_headers = false;
633 }
634 //Break this line up into several smaller lines if it's too long
635 //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
636 while (isset($line[self::MAX_LINE_LENGTH])) {
637 //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
638 //so as to avoid breaking in the middle of a word
639 $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
640 //Deliberately matches both false and 0
641 if (!$pos) {
642 //No nice break found, add a hard break
643 $pos = self::MAX_LINE_LENGTH - 1;
644 $lines_out[] = substr($line, 0, $pos);
645 $line = substr($line, $pos);
646 } else {
647 //Break at the found point
648 $lines_out[] = substr($line, 0, $pos);
649 //Move along by the amount we dealt with
650 $line = substr($line, $pos + 1);
651 }
652 //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
653 if ($in_headers) {
654 $line = "\t" . $line;
655 }
656 }
657 $lines_out[] = $line;
658
659 //Send the lines to the server
660 foreach ($lines_out as $line_out) {
661 //RFC2821 section 4.5.2
662 if (!empty($line_out) and $line_out[0] == '.') {
663 $line_out = '.' . $line_out;
664 }
665 $this->client_send($line_out . self::CRLF);
666 }
667 }
668
669 //Message data has been sent, complete the command
670 //Increase timelimit for end of DATA command
671 $savetimelimit = $this->Timelimit;
672 $this->Timelimit = $this->Timelimit * 2;
673 $result = $this->sendCommand('DATA END', '.', 250);
674 //Restore timelimit
675 $this->Timelimit = $savetimelimit;
676 return $result;
677 }
678
679 /**
680 * Send an SMTP HELO or EHLO command.
681 * Used to identify the sending server to the receiving server.
682 * This makes sure that client and server are in a known state.
683 * Implements RFC 821: HELO <SP> <domain> <CRLF>
684 * and RFC 2821 EHLO.
685 * @param string $host The host name or IP to connect to
686 * @access public
687 * @return boolean
688 */
689 public function hello($host = '')
690 {
691 //Try extended hello first (RFC 2821)
692 return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
693 }
694
695 /**
696 * Send an SMTP HELO or EHLO command.
697 * Low-level implementation used by hello()
698 * @see hello()
699 * @param string $hello The HELO string
700 * @param string $host The hostname to say we are
701 * @access protected
702 * @return boolean
703 */
704 protected function sendHello($hello, $host)
705 {
706 $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
707 $this->helo_rply = $this->last_reply;
708 if ($noerror) {
709 $this->parseHelloFields($hello);
710 } else {
711 $this->server_caps = null;
712 }
713 return $noerror;
714 }
715
716 /**
717 * Parse a reply to HELO/EHLO command to discover server extensions.
718 * In case of HELO, the only parameter that can be discovered is a server name.
719 * @access protected
720 * @param string $type - 'HELO' or 'EHLO'
721 */
722 protected function parseHelloFields($type)
723 {
724 $this->server_caps = array();
725 $lines = explode("\n", $this->last_reply);
726 foreach ($lines as $n => $s) {
727 $s = trim(substr($s, 4));
728 if (!$s) {
729 continue;
730 }
731 $fields = explode(' ', $s);
732 if (!empty($fields)) {
733 if (!$n) {
734 $name = $type;
735 $fields = $fields[0];
736 } else {
737 $name = array_shift($fields);
738 if ($name == 'SIZE') {
739 $fields = ($fields) ? $fields[0] : 0;
740 }
741 }
742 $this->server_caps[$name] = ($fields ? $fields : true);
743 }
744 }
745 }
746
747 /**
748 * Send an SMTP MAIL command.
749 * Starts a mail transaction from the email address specified in
750 * $from. Returns true if successful or false otherwise. If True
751 * the mail transaction is started and then one or more recipient
752 * commands may be called followed by a data command.
753 * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
754 * @param string $from Source address of this message
755 * @access public
756 * @return boolean
757 */
758 public function mail($from)
759 {
760 $useVerp = ($this->do_verp ? ' XVERP' : '');
761 return $this->sendCommand(
762 'MAIL FROM',
763 'MAIL FROM:<' . $from . '>' . $useVerp,
764 250
765 );
766 }
767
768 /**
769 * Send an SMTP QUIT command.
770 * Closes the socket if there is no error or the $close_on_error argument is true.
771 * Implements from rfc 821: QUIT <CRLF>
772 * @param boolean $close_on_error Should the connection close if an error occurs?
773 * @access public
774 * @return boolean
775 */
776 public function quit($close_on_error = true)
777 {
778 $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
779 $err = $this->error; //Save any error
780 if ($noerror or $close_on_error) {
781 $this->close();
782 $this->error = $err; //Restore any error from the quit command
783 }
784 return $noerror;
785 }
786
787 /**
788 * Send an SMTP RCPT command.
789 * Sets the TO argument to $toaddr.
790 * Returns true if the recipient was accepted false if it was rejected.
791 * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
792 * @param string $toaddr The address the message is being sent to
793 * @access public
794 * @return boolean
795 */
796 public function recipient($toaddr)
797 {
798 return $this->sendCommand(
799 'RCPT TO',
800 'RCPT TO:<' . $toaddr . '>',
801 array(250, 251)
802 );
803 }
804
805 /**
806 * Send an SMTP RSET command.
807 * Abort any transaction that is currently in progress.
808 * Implements rfc 821: RSET <CRLF>
809 * @access public
810 * @return boolean True on success.
811 */
812 public function reset()
813 {
814 return $this->sendCommand('RSET', 'RSET', 250);
815 }
816
817 /**
818 * Send a command to an SMTP server and check its return code.
819 * @param string $command The command name - not sent to the server
820 * @param string $commandstring The actual command to send
821 * @param integer|array $expect One or more expected integer success codes
822 * @access protected
823 * @return boolean True on success.
824 */
825 protected function sendCommand($command, $commandstring, $expect)
826 {
827 if (!$this->connected()) {
828 $this->setError("Called $command without being connected");
829 return false;
830 }
831 $this->client_send($commandstring . self::CRLF);
832
833 $this->last_reply = $this->get_lines();
834 // Fetch SMTP code and possible error code explanation
835 $matches = array();
836 if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) {
837 $code = $matches[1];
838 $code_ex = (count($matches) > 2 ? $matches[2] : null);
839 // Cut off error code from each response line
840 $detail = preg_replace(
841 "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m",
842 '',
843 $this->last_reply
844 );
845 } else {
846 // Fall back to simple parsing if regex fails
847 $code = substr($this->last_reply, 0, 3);
848 $code_ex = null;
849 $detail = substr($this->last_reply, 4);
850 }
851
852 $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
853
854 if (!in_array($code, (array)$expect)) {
855 $this->setError(
856 "$command command failed",
857 $detail,
858 $code,
859 $code_ex
860 );
861 $this->edebug(
862 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
863 self::DEBUG_CLIENT
864 );
865 return false;
866 }
867
868 $this->setError('');
869 return true;
870 }
871
872 /**
873 * Send an SMTP SAML command.
874 * Starts a mail transaction from the email address specified in $from.
875 * Returns true if successful or false otherwise. If True
876 * the mail transaction is started and then one or more recipient
877 * commands may be called followed by a data command. This command
878 * will send the message to the users terminal if they are logged
879 * in and send them an email.
880 * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
881 * @param string $from The address the message is from
882 * @access public
883 * @return boolean
884 */
885 public function sendAndMail($from)
886 {
887 return $this->sendCommand('SAML', "SAML FROM:$from", 250);
888 }
889
890 /**
891 * Send an SMTP VRFY command.
892 * @param string $name The name to verify
893 * @access public
894 * @return boolean
895 */
896 public function verify($name)
897 {
898 return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
899 }
900
901 /**
902 * Send an SMTP NOOP command.
903 * Used to keep keep-alives alive, doesn't actually do anything
904 * @access public
905 * @return boolean
906 */
907 public function noop()
908 {
909 return $this->sendCommand('NOOP', 'NOOP', 250);
910 }
911
912 /**
913 * Send an SMTP TURN command.
914 * This is an optional command for SMTP that this class does not support.
915 * This method is here to make the RFC821 Definition complete for this class
916 * and _may_ be implemented in future
917 * Implements from rfc 821: TURN <CRLF>
918 * @access public
919 * @return boolean
920 */
921 public function turn()
922 {
923 $this->setError('The SMTP TURN command is not implemented');
924 $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
925 return false;
926 }
927
928 /**
929 * Send raw data to the server.
930 * @param string $data The data to send
931 * @access public
932 * @return integer|boolean The number of bytes sent to the server or false on error
933 */
934 public function client_send($data)
935 {
936 $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT);
937 return fwrite($this->smtp_conn, $data);
938 }
939
940 /**
941 * Get the latest error.
942 * @access public
943 * @return array
944 */
945 public function getError()
946 {
947 return $this->error;
948 }
949
950 /**
951 * Get SMTP extensions available on the server
952 * @access public
953 * @return array|null
954 */
955 public function getServerExtList()
956 {
957 return $this->server_caps;
958 }
959
960 /**
961 * A multipurpose method
962 * The method works in three ways, dependent on argument value and current state
963 * 1. HELO/EHLO was not sent - returns null and set up $this->error
964 * 2. HELO was sent
965 * $name = 'HELO': returns server name
966 * $name = 'EHLO': returns boolean false
967 * $name = any string: returns null and set up $this->error
968 * 3. EHLO was sent
969 * $name = 'HELO'|'EHLO': returns server name
970 * $name = any string: if extension $name exists, returns boolean True
971 * or its options. Otherwise returns boolean False
972 * In other words, one can use this method to detect 3 conditions:
973 * - null returned: handshake was not or we don't know about ext (refer to $this->error)
974 * - false returned: the requested feature exactly not exists
975 * - positive value returned: the requested feature exists
976 * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
977 * @return mixed
978 */
979 public function getServerExt($name)
980 {
981 if (!$this->server_caps) {
982 $this->setError('No HELO/EHLO was sent');
983 return null;
984 }
985
986 // the tight logic knot ;)
987 if (!array_key_exists($name, $this->server_caps)) {
988 if ($name == 'HELO') {
989 return $this->server_caps['EHLO'];
990 }
991 if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) {
992 return false;
993 }
994 $this->setError('HELO handshake was used. Client knows nothing about server extensions');
995 return null;
996 }
997
998 return $this->server_caps[$name];
999 }
1000
1001 /**
1002 * Get the last reply from the server.
1003 * @access public
1004 * @return string
1005 */
1006 public function getLastReply()
1007 {
1008 return $this->last_reply;
1009 }
1010
1011 /**
1012 * Read the SMTP server's response.
1013 * Either before eof or socket timeout occurs on the operation.
1014 * With SMTP we can tell if we have more lines to read if the
1015 * 4th character is '-' symbol. If it is a space then we don't
1016 * need to read anything else.
1017 * @access protected
1018 * @return string
1019 */
1020 protected function get_lines()
1021 {
1022 // If the connection is bad, give up straight away
1023 if (!is_resource($this->smtp_conn)) {
1024 return '';
1025 }
1026 $data = '';
1027 $endtime = 0;
1028 stream_set_timeout($this->smtp_conn, $this->Timeout);
1029 if ($this->Timelimit > 0) {
1030 $endtime = time() + $this->Timelimit;
1031 }
1032 while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
1033 $str = @fgets($this->smtp_conn, 515);
1034 $this->edebug("SMTP -> get_lines(): \$data was \"$data\"", self::DEBUG_LOWLEVEL);
1035 $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL);
1036 $data .= $str;
1037 $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL);
1038 // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
1039 if ((isset($str[3]) and $str[3] == ' ')) {
1040 break;
1041 }
1042 // Timed-out? Log and break
1043 $info = stream_get_meta_data($this->smtp_conn);
1044 if ($info['timed_out']) {
1045 $this->edebug(
1046 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
1047 self::DEBUG_LOWLEVEL
1048 );
1049 break;
1050 }
1051 // Now check if reads took too long
1052 if ($endtime and time() > $endtime) {
1053 $this->edebug(
1054 'SMTP -> get_lines(): timelimit reached ('.
1055 $this->Timelimit . ' sec)',
1056 self::DEBUG_LOWLEVEL
1057 );
1058 break;
1059 }
1060 }
1061 return $data;
1062 }
1063
1064 /**
1065 * Enable or disable VERP address generation.
1066 * @param boolean $enabled
1067 */
1068 public function setVerp($enabled = false)
1069 {
1070 $this->do_verp = $enabled;
1071 }
1072
1073 /**
1074 * Get VERP address generation mode.
1075 * @return boolean
1076 */
1077 public function getVerp()
1078 {
1079 return $this->do_verp;
1080 }
1081
1082 /**
1083 * Set error messages and codes.
1084 * @param string $message The error message
1085 * @param string $detail Further detail on the error
1086 * @param string $smtp_code An associated SMTP error code
1087 * @param string $smtp_code_ex Extended SMTP code
1088 */
1089 protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
1090 {
1091 $this->error = array(
1092 'error' => $message,
1093 'detail' => $detail,
1094 'smtp_code' => $smtp_code,
1095 'smtp_code_ex' => $smtp_code_ex
1096 );
1097 }
1098
1099 /**
1100 * Set debug output method.
1101 * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it.
1102 */
1103 public function setDebugOutput($method = 'echo')
1104 {
1105 $this->Debugoutput = $method;
1106 }
1107
1108 /**
1109 * Get debug output method.
1110 * @return string
1111 */
1112 public function getDebugOutput()
1113 {
1114 return $this->Debugoutput;
1115 }
1116
1117 /**
1118 * Set debug output level.
1119 * @param integer $level
1120 */
1121 public function setDebugLevel($level = 0)
1122 {
1123 $this->do_debug = $level;
1124 }
1125
1126 /**
1127 * Get debug output level.
1128 * @return integer
1129 */
1130 public function getDebugLevel()
1131 {
1132 return $this->do_debug;
1133 }
1134
1135 /**
1136 * Set SMTP timeout.
1137 * @param integer $timeout
1138 */
1139 public function setTimeout($timeout = 0)
1140 {
1141 $this->Timeout = $timeout;
1142 }
1143
1144 /**
1145 * Get SMTP timeout.
1146 * @return integer
1147 */
1148 public function getTimeout()
1149 {
1150 return $this->Timeout;
1151 }
1152}
1153
1154/**
1155 * PHPMailer - PHP email creation and transport class.
1156 * PHP Version 5
1157 * @package PHPMailer
1158 * @link https://paypal.com The PHPMailer GitHub project
1159 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
1160 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
1161 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
1162 * @author Brent R. Matzelle (original founder)
1163 * @copyright 2012 - 2014 Marcus Bointon
1164 * @copyright 2010 - 2012 Jim Jagielski
1165 * @copyright 2004 - 2009 Andy Prevost
1166 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
1167 * @note This program is distributed in the hope that it will be useful - WITHOUT
1168 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1169 * FITNESS FOR A PARTICULAR PURPOSE.
1170 */
1171
1172/**
1173 * PHPMailer - PHP email creation and transport class.
1174 * @package PHPMailer
1175 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
1176 * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
1177 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
1178 * @author Brent R. Matzelle (original founder)
1179 */
1180class PHPMailer
1181{
1182 /**
1183 * The PHPMailer Version number.
1184 * @type string
1185 */
1186 public $Version = '5.2.10';
1187
1188 /**
1189 * Email priority.
1190 * Options: 1 = High, 3 = Normal, 5 = low.
1191 * @type integer
1192 */
1193 public $Priority = 3;
1194
1195 /**
1196 * The character set of the message.
1197 * @type string
1198 */
1199 public $CharSet = 'iso-8859-1';
1200
1201 /**
1202 * The MIME Content-type of the message.
1203 * @type string
1204 */
1205 public $ContentType = 'text/plain';
1206
1207 /**
1208 * The message encoding.
1209 * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
1210 * @type string
1211 */
1212 public $Encoding = '8bit';
1213
1214 /**
1215 * Holds the most recent mailer error message.
1216 * @type string
1217 */
1218 public $ErrorInfo = '';
1219
1220 /**
1221 * The From email address for the message.
1222 * @type string
1223 */
1224 public $From = 'root@localhost';
1225
1226 /**
1227 * The From name of the message.
1228 * @type string
1229 */
1230 public $FromName = 'Root User';
1231
1232 /**
1233 * The Sender email (Return-Path) of the message.
1234 * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
1235 * @type string
1236 */
1237 public $Sender = '';
1238
1239 /**
1240 * The Return-Path of the message.
1241 * If empty, it will be set to either From or Sender.
1242 * @type string
1243 * @deprecated Email senders should never set a return-path header;
1244 * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
1245 * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
1246 */
1247 public $ReturnPath = '';
1248
1249 /**
1250 * The Subject of the message.
1251 * @type string
1252 */
1253 public $Subject = '';
1254
1255 /**
1256 * An HTML or plain text message body.
1257 * If HTML then call isHTML(true).
1258 * @type string
1259 */
1260 public $Body = '';
1261
1262 /**
1263 * The plain-text message body.
1264 * This body can be read by mail clients that do not have HTML email
1265 * capability such as mutt & Eudora.
1266 * Clients that can read HTML will view the normal Body.
1267 * @type string
1268 */
1269 public $AltBody = '';
1270
1271 /**
1272 * An iCal message part body.
1273 * Only supported in simple alt or alt_inline message types
1274 * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
1275 * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
1276 * @link http://kigkonsult.se/iCalcreator/
1277 * @type string
1278 */
1279 public $Ical = '';
1280
1281 /**
1282 * The complete compiled MIME message body.
1283 * @access protected
1284 * @type string
1285 */
1286 protected $MIMEBody = '';
1287
1288 /**
1289 * The complete compiled MIME message headers.
1290 * @type string
1291 * @access protected
1292 */
1293 protected $MIMEHeader = '';
1294
1295 /**
1296 * Extra headers that createHeader() doesn't fold in.
1297 * @type string
1298 * @access protected
1299 */
1300 protected $mailHeader = '';
1301
1302 /**
1303 * Word-wrap the message body to this number of chars.
1304 * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
1305 * @type integer
1306 */
1307 public $WordWrap = 0;
1308
1309 /**
1310 * Which method to use to send mail.
1311 * Options: "mail", "sendmail", or "smtp".
1312 * @type string
1313 */
1314 public $Mailer = 'mail';
1315
1316 /**
1317 * The path to the sendmail program.
1318 * @type string
1319 */
1320 public $Sendmail = '/usr/sbin/sendmail';
1321
1322 /**
1323 * Whether mail() uses a fully sendmail-compatible MTA.
1324 * One which supports sendmail's "-oi -f" options.
1325 * @type boolean
1326 */
1327 public $UseSendmailOptions = true;
1328
1329 /**
1330 * Path to PHPMailer plugins.
1331 * Useful if the SMTP class is not in the PHP include path.
1332 * @type string
1333 * @deprecated Should not be needed now there is an autoloader.
1334 */
1335 public $PluginDir = '';
1336
1337 /**
1338 * The email address that a reading confirmation should be sent to.
1339 * @type string
1340 */
1341 public $ConfirmReadingTo = '';
1342
1343 /**
1344 * The hostname to use in Message-Id and Received headers
1345 * and as default HELO string.
1346 * If empty, the value returned
1347 * by SERVER_NAME is used or 'localhost.localdomain'.
1348 * @type string
1349 */
1350 public $Hostname = '';
1351
1352 /**
1353 * An ID to be used in the Message-Id header.
1354 * If empty, a unique id will be generated.
1355 * @type string
1356 */
1357 public $MessageID = '';
1358
1359 /**
1360 * The message Date to be used in the Date header.
1361 * If empty, the current date will be added.
1362 * @type string
1363 */
1364 public $MessageDate = '';
1365
1366 /**
1367 * SMTP hosts.
1368 * Either a single hostname or multiple semicolon-delimited hostnames.
1369 * You can also specify a different port
1370 * for each host by using this format: [hostname:port]
1371 * (e.g. "smtp1.example.com:25;smtp2.example.com").
1372 * You can also specify encryption type, for example:
1373 * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
1374 * Hosts will be tried in order.
1375 * @type string
1376 */
1377 public $Host = 'localhost';
1378
1379 /**
1380 * The default SMTP server port.
1381 * @type integer
1382 * @TODO Why is this needed when the SMTP class takes care of it?
1383 */
1384 public $Port = 25;
1385
1386 /**
1387 * The SMTP HELO of the message.
1388 * Default is $Hostname.
1389 * @type string
1390 * @see PHPMailer::$Hostname
1391 */
1392 public $Helo = '';
1393
1394 /**
1395 * What kind of encryption to use on the SMTP connection.
1396 * Options: '', 'ssl' or 'tls'
1397 * @type string
1398 */
1399 public $SMTPSecure = '';
1400
1401 /**
1402 * Whether to enable TLS encryption automatically if a server supports it,
1403 * even if `SMTPSecure` is not set to 'tls'.
1404 * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
1405 * @type boolean
1406 */
1407 public $SMTPAutoTLS = true;
1408
1409 /**
1410 * Whether to use SMTP authentication.
1411 * Uses the Username and Password properties.
1412 * @type boolean
1413 * @see PHPMailer::$Username
1414 * @see PHPMailer::$Password
1415 */
1416 public $SMTPAuth = false;
1417
1418 /**
1419 * Options array passed to stream_context_create when connecting via SMTP.
1420 * @type array
1421 */
1422 public $SMTPOptions = array();
1423
1424 /**
1425 * SMTP username.
1426 * @type string
1427 */
1428 public $Username = '';
1429
1430 /**
1431 * SMTP password.
1432 * @type string
1433 */
1434 public $Password = '';
1435
1436 /**
1437 * SMTP auth type.
1438 * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
1439 * @type string
1440 */
1441 public $AuthType = '';
1442
1443 /**
1444 * SMTP realm.
1445 * Used for NTLM auth
1446 * @type string
1447 */
1448 public $Realm = '';
1449
1450 /**
1451 * SMTP workstation.
1452 * Used for NTLM auth
1453 * @type string
1454 */
1455 public $Workstation = '';
1456
1457 /**
1458 * The SMTP server timeout in seconds.
1459 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
1460 * @type integer
1461 */
1462 public $Timeout = 300;
1463
1464 /**
1465 * SMTP class debug output mode.
1466 * Debug output level.
1467 * Options:
1468 * * `0` No output
1469 * * `1` Commands
1470 * * `2` Data and commands
1471 * * `3` As 2 plus connection status
1472 * * `4` Low-level data output
1473 * @type integer
1474 * @see SMTP::$do_debug
1475 */
1476 public $SMTPDebug = 0;
1477
1478 /**
1479 * How to handle debug output.
1480 * Options:
1481 * * `echo` Output plain-text as-is, appropriate for CLI
1482 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
1483 * * `error_log` Output to error log as configured in php.ini
1484 *
1485 * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
1486 * <code>
1487 * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
1488 * </code>
1489 * @type string|callable
1490 * @see SMTP::$Debugoutput
1491 */
1492 public $Debugoutput = 'echo';
1493
1494 /**
1495 * Whether to keep SMTP connection open after each message.
1496 * If this is set to true then to close the connection
1497 * requires an explicit call to smtpClose().
1498 * @type boolean
1499 */
1500 public $SMTPKeepAlive = false;
1501
1502 /**
1503 * Whether to split multiple to addresses into multiple messages
1504 * or send them all in one message.
1505 * @type boolean
1506 */
1507 public $SingleTo = false;
1508
1509 /**
1510 * Storage for addresses when SingleTo is enabled.
1511 * @type array
1512 * @TODO This should really not be public
1513 */
1514 public $SingleToArray = array();
1515
1516 /**
1517 * Whether to generate VERP addresses on send.
1518 * Only applicable when sending via SMTP.
1519 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
1520 * @link http://www.postfix.org/VERP_README.html Postfix VERP info
1521 * @type boolean
1522 */
1523 public $do_verp = false;
1524
1525 /**
1526 * Whether to allow sending messages with an empty body.
1527 * @type boolean
1528 */
1529 public $AllowEmpty = false;
1530
1531 /**
1532 * The default line ending.
1533 * @note The default remains "\n". We force CRLF where we know
1534 * it must be used via self::CRLF.
1535 * @type string
1536 */
1537 public $LE = "\n";
1538
1539 /**
1540 * DKIM selector.
1541 * @type string
1542 */
1543 public $DKIM_selector = '';
1544
1545 /**
1546 * DKIM Identity.
1547 * Usually the email address used as the source of the email
1548 * @type string
1549 */
1550 public $DKIM_identity = '';
1551
1552 /**
1553 * DKIM passphrase.
1554 * Used if your key is encrypted.
1555 * @type string
1556 */
1557 public $DKIM_passphrase = '';
1558
1559 /**
1560 * DKIM signing domain name.
1561 * @example 'example.com'
1562 * @type string
1563 */
1564 public $DKIM_domain = '';
1565
1566 /**
1567 * DKIM private key file path.
1568 * @type string
1569 */
1570 public $DKIM_private = '';
1571
1572 /**
1573 * Callback Action function name.
1574 *
1575 * The function that handles the result of the send email action.
1576 * It is called out by send() for each email sent.
1577 *
1578 * Value can be any php callable: http://www.php.net/is_callable
1579 *
1580 * Parameters:
1581 * boolean $result result of the send action
1582 * string $to email address of the recipient
1583 * string $cc cc email addresses
1584 * string $bcc bcc email addresses
1585 * string $subject the subject
1586 * string $body the email body
1587 * string $from email address of sender
1588 * @type string
1589 */
1590 public $action_function = '';
1591
1592 /**
1593 * What to put in the X-Mailer header.
1594 * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
1595 * @type string
1596 */
1597 public $XMailer = '';
1598
1599 /**
1600 * An instance of the SMTP sender class.
1601 * @type SMTP
1602 * @access protected
1603 */
1604 protected $smtp = null;
1605
1606 /**
1607 * The array of 'to' addresses.
1608 * @type array
1609 * @access protected
1610 */
1611 protected $to = array();
1612
1613 /**
1614 * The array of 'cc' addresses.
1615 * @type array
1616 * @access protected
1617 */
1618 protected $cc = array();
1619
1620 /**
1621 * The array of 'bcc' addresses.
1622 * @type array
1623 * @access protected
1624 */
1625 protected $bcc = array();
1626
1627 /**
1628 * The array of reply-to names and addresses.
1629 * @type array
1630 * @access protected
1631 */
1632 protected $ReplyTo = array();
1633
1634 /**
1635 * An array of all kinds of addresses.
1636 * Includes all of $to, $cc, $bcc
1637 * @type array
1638 * @access protected
1639 */
1640 protected $all_recipients = array();
1641
1642 /**
1643 * The array of attachments.
1644 * @type array
1645 * @access protected
1646 */
1647 protected $attachment = array();
1648
1649 /**
1650 * The array of custom headers.
1651 * @type array
1652 * @access protected
1653 */
1654 protected $CustomHeader = array();
1655
1656 /**
1657 * The most recent Message-ID (including angular brackets).
1658 * @type string
1659 * @access protected
1660 */
1661 protected $lastMessageID = '';
1662
1663 /**
1664 * The message's MIME type.
1665 * @type string
1666 * @access protected
1667 */
1668 protected $message_type = '';
1669
1670 /**
1671 * The array of MIME boundary strings.
1672 * @type array
1673 * @access protected
1674 */
1675 protected $boundary = array();
1676
1677 /**
1678 * The array of available languages.
1679 * @type array
1680 * @access protected
1681 */
1682 protected $language = array();
1683
1684 /**
1685 * The number of errors encountered.
1686 * @type integer
1687 * @access protected
1688 */
1689 protected $error_count = 0;
1690
1691 /**
1692 * The S/MIME certificate file path.
1693 * @type string
1694 * @access protected
1695 */
1696 protected $sign_cert_file = '';
1697
1698 /**
1699 * The S/MIME key file path.
1700 * @type string
1701 * @access protected
1702 */
1703 protected $sign_key_file = '';
1704
1705 /**
1706 * The optional S/MIME extra certificates ("CA Chain") file path.
1707 * @type string
1708 * @access protected
1709 */
1710 protected $sign_extracerts_file = '';
1711
1712 /**
1713 * The S/MIME password for the key.
1714 * Used only if the key is encrypted.
1715 * @type string
1716 * @access protected
1717 */
1718 protected $sign_key_pass = '';
1719
1720 /**
1721 * Whether to throw exceptions for errors.
1722 * @type boolean
1723 * @access protected
1724 */
1725 protected $exceptions = false;
1726
1727 /**
1728 * Unique ID used for message ID and boundaries.
1729 * @type string
1730 * @access protected
1731 */
1732 protected $uniqueid = '';
1733
1734 /**
1735 * Error severity: message only, continue processing.
1736 */
1737 const STOP_MESSAGE = 0;
1738
1739 /**
1740 * Error severity: message, likely ok to continue processing.
1741 */
1742 const STOP_CONTINUE = 1;
1743
1744 /**
1745 * Error severity: message, plus full stop, critical error reached.
1746 */
1747 const STOP_CRITICAL = 2;
1748
1749 /**
1750 * SMTP RFC standard line ending.
1751 */
1752 const CRLF = "\r\n";
1753
1754 /**
1755 * The maximum line length allowed by RFC 2822 section 2.1.1
1756 * @type integer
1757 */
1758 const MAX_LINE_LENGTH = 998;
1759
1760 /**
1761 * Constructor.
1762 * @param boolean $exceptions Should we throw external exceptions?
1763 */
1764 public function __construct($exceptions = false)
1765 {
1766 $this->exceptions = (boolean)$exceptions;
1767 }
1768
1769 /**
1770 * Destructor.
1771 */
1772 public function __destruct()
1773 {
1774 //Close any open SMTP connection nicely
1775 if ($this->Mailer == 'smtp') {
1776 $this->smtpClose();
1777 }
1778 }
1779
1780 /**
1781 * Call mail() in a safe_mode-aware fashion.
1782 * Also, unless sendmail_path points to sendmail (or something that
1783 * claims to be sendmail), don't pass params (not a perfect fix,
1784 * but it will do)
1785 * @param string $to To
1786 * @param string $subject Subject
1787 * @param string $body Message Body
1788 * @param string $header Additional Header(s)
1789 * @param string $params Params
1790 * @access private
1791 * @return boolean
1792 */
1793 private function mailPassthru($to, $subject, $body, $header, $params)
1794 {
1795 //Check overloading of mail function to avoid double-encoding
1796 if (ini_get('mbstring.func_overload') & 1) {
1797 $subject = $this->secureHeader($subject);
1798 } else {
1799 $subject = $this->encodeHeader($this->secureHeader($subject));
1800 }
1801 if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
1802 $result = @mail($to, $subject, $body, $header);
1803 } else {
1804 $result = @mail($to, $subject, $body, $header, $params);
1805 }
1806 return $result;
1807 }
1808
1809 /**
1810 * Output debugging info via user-defined method.
1811 * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
1812 * @see PHPMailer::$Debugoutput
1813 * @see PHPMailer::$SMTPDebug
1814 * @param string $str
1815 */
1816 protected function edebug($str)
1817 {
1818 if ($this->SMTPDebug <= 0) {
1819 return;
1820 }
1821 //Avoid clash with built-in function names
1822 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
1823 call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
1824 return;
1825 }
1826 switch ($this->Debugoutput) {
1827 case 'error_log':
1828 //Don't output, just log
1829 error_log($str);
1830 break;
1831 case 'html':
1832 //Cleans up output a bit for a better looking, HTML-safe output
1833 echo htmlentities(
1834 preg_replace('/[\r\n]+/', '', $str),
1835 ENT_QUOTES,
1836 'UTF-8'
1837 )
1838 . "<br>\n";
1839 break;
1840 case 'echo':
1841 default:
1842 //Normalize line breaks
1843 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
1844 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
1845 "\n",
1846 "\n \t ",
1847 trim($str)
1848 ) . "\n";
1849 }
1850 }
1851
1852 /**
1853 * Sets message type to HTML or plain.
1854 * @param boolean $isHtml True for HTML mode.
1855 * @return void
1856 */
1857 public function isHTML($isHtml = true)
1858 {
1859 if ($isHtml) {
1860 $this->ContentType = 'text/html';
1861 } else {
1862 $this->ContentType = 'text/plain';
1863 }
1864 }
1865
1866 /**
1867 * Send messages using SMTP.
1868 * @return void
1869 */
1870 public function isSMTP()
1871 {
1872 $this->Mailer = 'smtp';
1873 }
1874
1875 /**
1876 * Send messages using PHP's mail() function.
1877 * @return void
1878 */
1879 public function isMail()
1880 {
1881 $this->Mailer = 'mail';
1882 }
1883
1884 /**
1885 * Send messages using $Sendmail.
1886 * @return void
1887 */
1888 public function isSendmail()
1889 {
1890 $ini_sendmail_path = ini_get('sendmail_path');
1891
1892 if (!stristr($ini_sendmail_path, 'sendmail')) {
1893 $this->Sendmail = '/usr/sbin/sendmail';
1894 } else {
1895 $this->Sendmail = $ini_sendmail_path;
1896 }
1897 $this->Mailer = 'sendmail';
1898 }
1899
1900 /**
1901 * Send messages using qmail.
1902 * @return void
1903 */
1904 public function isQmail()
1905 {
1906 $ini_sendmail_path = ini_get('sendmail_path');
1907
1908 if (!stristr($ini_sendmail_path, 'qmail')) {
1909 $this->Sendmail = '/var/qmail/bin/qmail-inject';
1910 } else {
1911 $this->Sendmail = $ini_sendmail_path;
1912 }
1913 $this->Mailer = 'qmail';
1914 }
1915
1916 /**
1917 * Add a "To" address.
1918 * @param string $address
1919 * @param string $name
1920 * @return boolean true on success, false if address already used
1921 */
1922 public function addAddress($address, $name = '')
1923 {
1924 return $this->addAnAddress('to', $address, $name);
1925 }
1926
1927 /**
1928 * Add a "CC" address.
1929 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
1930 * @param string $address
1931 * @param string $name
1932 * @return boolean true on success, false if address already used
1933 */
1934 public function addCC($address, $name = '')
1935 {
1936 return $this->addAnAddress('cc', $address, $name);
1937 }
1938
1939 /**
1940 * Add a "BCC" address.
1941 * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
1942 * @param string $address
1943 * @param string $name
1944 * @return boolean true on success, false if address already used
1945 */
1946 public function addBCC($address, $name = '')
1947 {
1948 return $this->addAnAddress('bcc', $address, $name);
1949 }
1950
1951 /**
1952 * Add a "Reply-to" address.
1953 * @param string $address
1954 * @param string $name
1955 * @return boolean
1956 */
1957 public function addReplyTo($address, $name = '')
1958 {
1959 return $this->addAnAddress('Reply-To', $address, $name);
1960 }
1961
1962 /**
1963 * Add an address to one of the recipient arrays.
1964 * Addresses that have been added already return false, but do not throw exceptions
1965 * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
1966 * @param string $address The email address to send to
1967 * @param string $name
1968 * @throws phpmailerException
1969 * @return boolean true on success, false if address already used or invalid in some way
1970 * @access protected
1971 */
1972 protected function addAnAddress($kind, $address, $name = '')
1973 {
1974 if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
1975 $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
1976 $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
1977 if ($this->exceptions) {
1978 throw new phpmailerException('Invalid recipient array: ' . $kind);
1979 }
1980 return false;
1981 }
1982 $address = trim($address);
1983 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
1984 if (!$this->validateAddress($address)) {
1985 $this->setError($this->lang('invalid_address') . ': ' . $address);
1986 $this->edebug($this->lang('invalid_address') . ': ' . $address);
1987 if ($this->exceptions) {
1988 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
1989 }
1990 return false;
1991 }
1992 if ($kind != 'Reply-To') {
1993 if (!isset($this->all_recipients[strtolower($address)])) {
1994 array_push($this->$kind, array($address, $name));
1995 $this->all_recipients[strtolower($address)] = true;
1996 return true;
1997 }
1998 } else {
1999 if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
2000 $this->ReplyTo[strtolower($address)] = array($address, $name);
2001 return true;
2002 }
2003 }
2004 return false;
2005 }
2006
2007 /**
2008 * Set the From and FromName properties.
2009 * @param string $address
2010 * @param string $name
2011 * @param boolean $auto Whether to also set the Sender address, defaults to true
2012 * @throws phpmailerException
2013 * @return boolean
2014 */
2015 public function setFrom($address, $name = '', $auto = true)
2016 {
2017 $address = trim($address);
2018 $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
2019 if (!$this->validateAddress($address)) {
2020 $this->setError($this->lang('invalid_address') . ': ' . $address);
2021 $this->edebug($this->lang('invalid_address') . ': ' . $address);
2022 if ($this->exceptions) {
2023 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
2024 }
2025 return false;
2026 }
2027 $this->From = $address;
2028 $this->FromName = $name;
2029 if ($auto) {
2030 if (empty($this->Sender)) {
2031 $this->Sender = $address;
2032 }
2033 }
2034 return true;
2035 }
2036
2037 /**
2038 * Return the Message-ID header of the last email.
2039 * Technically this is the value from the last time the headers were created,
2040 * but it's also the message ID of the last sent message except in
2041 * pathological cases.
2042 * @return string
2043 */
2044 public function getLastMessageID()
2045 {
2046 return $this->lastMessageID;
2047 }
2048
2049 /**
2050 * Check that a string looks like an email address.
2051 * @param string $address The email address to check
2052 * @param string $patternselect A selector for the validation pattern to use :
2053 * * `auto` Pick strictest one automatically;
2054 * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
2055 * * `pcre` Use old PCRE implementation;
2056 * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; same as pcre8 but does not allow 'dotless' domains;
2057 * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
2058 * * `noregex` Don't use a regex: super fast, really dumb.
2059 * @return boolean
2060 * @static
2061 * @access public
2062 */
2063 public static function validateAddress($address, $patternselect = 'auto')
2064 {
2065 if (!$patternselect or $patternselect == 'auto') {
2066 //Check this constant first so it works when extension_loaded() is disabled by safe mode
2067 //Constant was added in PHP 5.2.4
2068 if (defined('PCRE_VERSION')) {
2069 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
2070 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
2071 $patternselect = 'pcre8';
2072 } else {
2073 $patternselect = 'pcre';
2074 }
2075 } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
2076 //Fall back to older PCRE
2077 $patternselect = 'pcre';
2078 } else {
2079 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
2080 if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
2081 $patternselect = 'php';
2082 } else {
2083 $patternselect = 'noregex';
2084 }
2085 }
2086 }
2087 switch ($patternselect) {
2088 case 'pcre8':
2089 /**
2090 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
2091 * @link http://squiloople.com/2009/12/20/email-address-validation/
2092 * @copyright 2009-2010 Michael Rushton
2093 * Feel free to use and redistribute this code. But please keep this copyright notice.
2094 */
2095 return (boolean)preg_match(
2096 '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
2097 '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
2098 '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
2099 '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
2100 '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
2101 '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
2102 '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
2103 '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
2104 '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
2105 $address
2106 );
2107 case 'pcre':
2108 //An older regex that doesn't need a recent PCRE
2109 return (boolean)preg_match(
2110 '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
2111 '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
2112 '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
2113 '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
2114 '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
2115 '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
2116 '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
2117 '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
2118 '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
2119 '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
2120 $address
2121 );
2122 case 'html5':
2123 /**
2124 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
2125 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
2126 */
2127 return (boolean)preg_match(
2128 '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
2129 '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
2130 $address
2131 );
2132 case 'noregex':
2133 //No PCRE! Do something _very_ approximate!
2134 //Check the address is 3 chars or longer and contains an @ that's not the first or last char
2135 return (strlen($address) >= 3
2136 and strpos($address, '@') >= 1
2137 and strpos($address, '@') != strlen($address) - 1);
2138 case 'php':
2139 default:
2140 return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
2141 }
2142 }
2143
2144 /**
2145 * Create a message and send it.
2146 * Uses the sending method specified by $Mailer.
2147 * @throws phpmailerException
2148 * @return boolean false on error - See the ErrorInfo property for details of the error.
2149 */
2150 public function send()
2151 {
2152 try {
2153 if (!$this->preSend()) {
2154 return false;
2155 }
2156 return $this->postSend();
2157 } catch (phpmailerException $exc) {
2158 $this->mailHeader = '';
2159 $this->setError($exc->getMessage());
2160 if ($this->exceptions) {
2161 throw $exc;
2162 }
2163 return false;
2164 }
2165 }
2166
2167 /**
2168 * Prepare a message for sending.
2169 * @throws phpmailerException
2170 * @return boolean
2171 */
2172 public function preSend()
2173 {
2174 try {
2175 $this->mailHeader = '';
2176 if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
2177 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
2178 }
2179
2180 // Set whether the message is multipart/alternative
2181 if (!empty($this->AltBody)) {
2182 $this->ContentType = 'multipart/alternative';
2183 }
2184
2185 $this->error_count = 0; // Reset errors
2186 $this->setMessageType();
2187 // Refuse to send an empty message unless we are specifically allowing it
2188 if (!$this->AllowEmpty and empty($this->Body)) {
2189 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
2190 }
2191
2192 // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
2193 $this->MIMEHeader = '';
2194 $this->MIMEBody = $this->createBody();
2195 // createBody may have added some headers, so retain them
2196 $tempheaders = $this->MIMEHeader;
2197 $this->MIMEHeader = $this->createHeader();
2198 $this->MIMEHeader .= $tempheaders;
2199
2200 // To capture the complete message when using mail(), create
2201 // an extra header list which createHeader() doesn't fold in
2202 if ($this->Mailer == 'mail') {
2203 if (count($this->to) > 0) {
2204 $this->mailHeader .= $this->addrAppend('To', $this->to);
2205 } else {
2206 $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
2207 }
2208 $this->mailHeader .= $this->headerLine(
2209 'Subject',
2210 $this->encodeHeader($this->secureHeader(trim($this->Subject)))
2211 );
2212 }
2213
2214 // Sign with DKIM if enabled
2215 if (!empty($this->DKIM_domain)
2216 && !empty($this->DKIM_private)
2217 && !empty($this->DKIM_selector)
2218 && file_exists($this->DKIM_private)) {
2219 $header_dkim = $this->DKIM_Add(
2220 $this->MIMEHeader . $this->mailHeader,
2221 $this->encodeHeader($this->secureHeader($this->Subject)),
2222 $this->MIMEBody
2223 );
2224 $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
2225 str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
2226 }
2227 return true;
2228 } catch (phpmailerException $exc) {
2229 $this->setError($exc->getMessage());
2230 if ($this->exceptions) {
2231 throw $exc;
2232 }
2233 return false;
2234 }
2235 }
2236
2237 /**
2238 * Actually send a message.
2239 * Send the email via the selected mechanism
2240 * @throws phpmailerException
2241 * @return boolean
2242 */
2243 public function postSend()
2244 {
2245 try {
2246 // Choose the mailer and send through it
2247 switch ($this->Mailer) {
2248 case 'sendmail':
2249 case 'qmail':
2250 return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
2251 case 'smtp':
2252 return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
2253 case 'mail':
2254 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
2255 default:
2256 $sendMethod = $this->Mailer.'Send';
2257 if (method_exists($this, $sendMethod)) {
2258 return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
2259 }
2260
2261 return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
2262 }
2263 } catch (phpmailerException $exc) {
2264 $this->setError($exc->getMessage());
2265 $this->edebug($exc->getMessage());
2266 if ($this->exceptions) {
2267 throw $exc;
2268 }
2269 }
2270 return false;
2271 }
2272
2273 /**
2274 * Send mail using the $Sendmail program.
2275 * @param string $header The message headers
2276 * @param string $body The message body
2277 * @see PHPMailer::$Sendmail
2278 * @throws phpmailerException
2279 * @access protected
2280 * @return boolean
2281 */
2282 protected function sendmailSend($header, $body)
2283 {
2284 if ($this->Sender != '') {
2285 if ($this->Mailer == 'qmail') {
2286 $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
2287 } else {
2288 $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
2289 }
2290 } else {
2291 if ($this->Mailer == 'qmail') {
2292 $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
2293 } else {
2294 $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
2295 }
2296 }
2297 if ($this->SingleTo) {
2298 foreach ($this->SingleToArray as $toAddr) {
2299 if (!@$mail = popen($sendmail, 'w')) {
2300 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
2301 }
2302 fputs($mail, 'To: ' . $toAddr . "\n");
2303 fputs($mail, $header);
2304 fputs($mail, $body);
2305 $result = pclose($mail);
2306 $this->doCallback(
2307 ($result == 0),
2308 array($toAddr),
2309 $this->cc,
2310 $this->bcc,
2311 $this->Subject,
2312 $body,
2313 $this->From
2314 );
2315 if ($result != 0) {
2316 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
2317 }
2318 }
2319 } else {
2320 if (!@$mail = popen($sendmail, 'w')) {
2321 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
2322 }
2323 fputs($mail, $header);
2324 fputs($mail, $body);
2325 $result = pclose($mail);
2326 $this->doCallback(($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
2327 if ($result != 0) {
2328 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
2329 }
2330 }
2331 return true;
2332 }
2333
2334 /**
2335 * Send mail using the PHP mail() function.
2336 * @param string $header The message headers
2337 * @param string $body The message body
2338 * @link http://www.php.net/manual/en/book.mail.php
2339 * @throws phpmailerException
2340 * @access protected
2341 * @return boolean
2342 */
2343 protected function mailSend($header, $body)
2344 {
2345 $toArr = array();
2346 foreach ($this->to as $toaddr) {
2347 $toArr[] = $this->addrFormat($toaddr);
2348 }
2349 $to = implode(', ', $toArr);
2350
2351 if (empty($this->Sender)) {
2352 $params = ' ';
2353 } else {
2354 $params = sprintf('-f%s', $this->Sender);
2355 }
2356 if ($this->Sender != '' and !ini_get('safe_mode')) {
2357 $old_from = ini_get('sendmail_from');
2358 ini_set('sendmail_from', $this->Sender);
2359 }
2360 $result = false;
2361 if ($this->SingleTo && count($toArr) > 1) {
2362 foreach ($toArr as $toAddr) {
2363 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
2364 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
2365 }
2366 } else {
2367 $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
2368 $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
2369 }
2370 if (isset($old_from)) {
2371 ini_set('sendmail_from', $old_from);
2372 }
2373 if (!$result) {
2374 throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
2375 }
2376 return true;
2377 }
2378
2379 /**
2380 * Get an instance to use for SMTP operations.
2381 * Override this function to load your own SMTP implementation
2382 * @return SMTP
2383 */
2384 public function getSMTPInstance()
2385 {
2386 if (!is_object($this->smtp)) {
2387 $this->smtp = new SMTP;
2388 }
2389 return $this->smtp;
2390 }
2391
2392 /**
2393 * Send mail via SMTP.
2394 * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
2395 * Uses the PHPMailerSMTP class by default.
2396 * @see PHPMailer::getSMTPInstance() to use a different class.
2397 * @param string $header The message headers
2398 * @param string $body The message body
2399 * @throws phpmailerException
2400 * @uses SMTP
2401 * @access protected
2402 * @return boolean
2403 */
2404 protected function smtpSend($header, $body)
2405 {
2406 $bad_rcpt = array();
2407 if (!$this->smtpConnect($this->SMTPOptions)) {
2408 throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
2409 }
2410 if ('' == $this->Sender) {
2411 $smtp_from = $this->From;
2412 } else {
2413 $smtp_from = $this->Sender;
2414 }
2415 if (!$this->smtp->mail($smtp_from)) {
2416 $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
2417 throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
2418 }
2419
2420 // Attempt to send to all recipients
2421 foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
2422 foreach ($togroup as $to) {
2423 if (!$this->smtp->recipient($to[0])) {
2424 $error = $this->smtp->getError();
2425 $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
2426 $isSent = false;
2427 } else {
2428 $isSent = true;
2429 }
2430 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
2431 }
2432 }
2433
2434 // Only send the DATA command if we have viable recipients
2435 if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
2436 throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
2437 }
2438 if ($this->SMTPKeepAlive) {
2439 $this->smtp->reset();
2440 } else {
2441 $this->smtp->quit();
2442 $this->smtp->close();
2443 }
2444 //Create error message for any bad addresses
2445 if (count($bad_rcpt) > 0) {
2446 $errstr = '';
2447 foreach ($bad_rcpt as $bad) {
2448 $errstr .= $bad['to'] . ': ' . $bad['error'];
2449 }
2450 throw new phpmailerException(
2451 $this->lang('recipients_failed') . $errstr,
2452 self::STOP_CONTINUE
2453 );
2454 }
2455 return true;
2456 }
2457
2458 /**
2459 * Initiate a connection to an SMTP server.
2460 * Returns false if the operation failed.
2461 * @param array $options An array of options compatible with stream_context_create()
2462 * @uses SMTP
2463 * @access public
2464 * @throws phpmailerException
2465 * @return boolean
2466 */
2467 public function smtpConnect($options = array())
2468 {
2469 if (is_null($this->smtp)) {
2470 $this->smtp = $this->getSMTPInstance();
2471 }
2472
2473 // Already connected?
2474 if ($this->smtp->connected()) {
2475 return true;
2476 }
2477
2478 $this->smtp->setTimeout($this->Timeout);
2479 $this->smtp->setDebugLevel($this->SMTPDebug);
2480 $this->smtp->setDebugOutput($this->Debugoutput);
2481 $this->smtp->setVerp($this->do_verp);
2482 $hosts = explode(';', $this->Host);
2483 $lastexception = null;
2484
2485 foreach ($hosts as $hostentry) {
2486 $hostinfo = array();
2487 if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
2488 // Not a valid host entry
2489 continue;
2490 }
2491 // $hostinfo[2]: optional ssl or tls prefix
2492 // $hostinfo[3]: the hostname
2493 // $hostinfo[4]: optional port number
2494 // The host string prefix can temporarily override the current setting for SMTPSecure
2495 // If it's not specified, the default value is used
2496 $prefix = '';
2497 $secure = $this->SMTPSecure;
2498 $tls = ($this->SMTPSecure == 'tls');
2499 if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
2500 $prefix = 'ssl://';
2501 $tls = false; // Can't have SSL and TLS at the same time
2502 $secure = 'ssl';
2503 } elseif ($hostinfo[2] == 'tls') {
2504 $tls = true;
2505 // tls doesn't use a prefix
2506 $secure = 'tls';
2507 }
2508 //Do we need the OpenSSL extension?
2509 $sslext = defined('OPENSSL_ALGO_SHA1');
2510 if ('tls' === $secure or 'ssl' === $secure) {
2511 //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
2512 if (!$sslext) {
2513 throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
2514 }
2515 }
2516 $host = $hostinfo[3];
2517 $port = $this->Port;
2518 $tport = (integer)$hostinfo[4];
2519 if ($tport > 0 and $tport < 65536) {
2520 $port = $tport;
2521 }
2522 if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
2523 try {
2524 if ($this->Helo) {
2525 $hello = $this->Helo;
2526 } else {
2527 $hello = $this->serverHostname();
2528 }
2529 $this->smtp->hello($hello);
2530 //Automatically enable TLS encryption if:
2531 // * it's not disabled
2532 // * we have openssl extension
2533 // * we are not already using SSL
2534 // * the server offers STARTTLS
2535 if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
2536 $tls = true;
2537 }
2538 if ($tls) {
2539 if (!$this->smtp->startTLS()) {
2540 throw new phpmailerException($this->lang('connect_host'));
2541 }
2542 // We must resend HELO after tls negotiation
2543 $this->smtp->hello($hello);
2544 }
2545 if ($this->SMTPAuth) {
2546 if (!$this->smtp->authenticate(
2547 $this->Username,
2548 $this->Password,
2549 $this->AuthType,
2550 $this->Realm,
2551 $this->Workstation
2552 )
2553 ) {
2554 throw new phpmailerException($this->lang('authenticate'));
2555 }
2556 }
2557 return true;
2558 } catch (phpmailerException $exc) {
2559 $lastexception = $exc;
2560 $this->edebug($exc->getMessage());
2561 // We must have connected, but then failed TLS or Auth, so close connection nicely
2562 $this->smtp->quit();
2563 }
2564 }
2565 }
2566 // If we get here, all connection attempts have failed, so close connection hard
2567 $this->smtp->close();
2568 // As we've caught all exceptions, just report whatever the last one was
2569 if ($this->exceptions and !is_null($lastexception)) {
2570 throw $lastexception;
2571 }
2572 return false;
2573 }
2574
2575 /**
2576 * Close the active SMTP session if one exists.
2577 * @return void
2578 */
2579 public function smtpClose()
2580 {
2581 if ($this->smtp !== null) {
2582 if ($this->smtp->connected()) {
2583 $this->smtp->quit();
2584 $this->smtp->close();
2585 }
2586 }
2587 }
2588
2589 /**
2590 * Set the language for error messages.
2591 * Returns false if it cannot load the language file.
2592 * The default language is English.
2593 * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
2594 * @param string $lang_path Path to the language file directory, with trailing separator (slash)
2595 * @return boolean
2596 * @access public
2597 */
2598 public function setLanguage($langcode = 'en', $lang_path = '')
2599 {
2600 // Define full set of translatable strings in English
2601 $PHPMAILER_LANG = array(
2602 'authenticate' => 'SMTP Error: Could not authenticate.',
2603 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
2604 'data_not_accepted' => 'SMTP Error: data not accepted.',
2605 'empty_message' => 'Message body empty',
2606 'encoding' => 'Unknown encoding: ',
2607 'execute' => 'Could not execute: ',
2608 'file_access' => 'Could not access file: ',
2609 'file_open' => 'File Error: Could not open file: ',
2610 'from_failed' => 'The following From address failed: ',
2611 'instantiate' => 'Could not instantiate mail function.',
2612 'invalid_address' => 'Invalid address',
2613 'mailer_not_supported' => ' mailer is not supported.',
2614 'provide_address' => 'You must provide at least one recipient email address.',
2615 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
2616 'signing' => 'Signing Error: ',
2617 'smtp_connect_failed' => 'SMTP connect() failed.',
2618 'smtp_error' => 'SMTP server error: ',
2619 'variable_set' => 'Cannot set or reset variable: ',
2620 'extension_missing' => 'Extension missing: '
2621 );
2622 if (empty($lang_path)) {
2623 // Calculate an absolute path so it can work if CWD is not here
2624 $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
2625 }
2626 $foundlang = true;
2627 $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
2628 // There is no English translation file
2629 if ($langcode != 'en') {
2630 // Make sure language file path is readable
2631 if (!is_readable($lang_file)) {
2632 $foundlang = false;
2633 } else {
2634 // Overwrite language-specific strings.
2635 // This way we'll never have missing translation keys.
2636 $foundlang = include $lang_file;
2637 }
2638 }
2639 $this->language = $PHPMAILER_LANG;
2640 return (boolean)$foundlang; // Returns false if language not found
2641 }
2642
2643 /**
2644 * Get the array of strings for the current language.
2645 * @return array
2646 */
2647 public function getTranslations()
2648 {
2649 return $this->language;
2650 }
2651
2652 /**
2653 * Create recipient headers.
2654 * @access public
2655 * @param string $type
2656 * @param array $addr An array of recipient,
2657 * where each recipient is a 2-element indexed array with element 0 containing an address
2658 * and element 1 containing a name, like:
2659 * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
2660 * @return string
2661 */
2662 public function addrAppend($type, $addr)
2663 {
2664 $addresses = array();
2665 foreach ($addr as $address) {
2666 $addresses[] = $this->addrFormat($address);
2667 }
2668 return $type . ': ' . implode(', ', $addresses) . $this->LE;
2669 }
2670
2671 /**
2672 * Format an address for use in a message header.
2673 * @access public
2674 * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
2675 * like array('joe@example.com', 'Joe User')
2676 * @return string
2677 */
2678 public function addrFormat($addr)
2679 {
2680 if (empty($addr[1])) { // No name provided
2681 return $this->secureHeader($addr[0]);
2682 } else {
2683 return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
2684 $addr[0]
2685 ) . '>';
2686 }
2687 }
2688
2689 /**
2690 * Word-wrap message.
2691 * For use with mailers that do not automatically perform wrapping
2692 * and for quoted-printable encoded messages.
2693 * Original written by philippe.
2694 * @param string $message The message to wrap
2695 * @param integer $length The line length to wrap to
2696 * @param boolean $qp_mode Whether to run in Quoted-Printable mode
2697 * @access public
2698 * @return string
2699 */
2700 public function wrapText($message, $length, $qp_mode = false)
2701 {
2702 if ($qp_mode) {
2703 $soft_break = sprintf(' =%s', $this->LE);
2704 } else {
2705 $soft_break = $this->LE;
2706 }
2707 // If utf-8 encoding is used, we will need to make sure we don't
2708 // split multibyte characters when we wrap
2709 $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
2710 $lelen = strlen($this->LE);
2711 $crlflen = strlen(self::CRLF);
2712
2713 $message = $this->fixEOL($message);
2714 //Remove a trailing line break
2715 if (substr($message, -$lelen) == $this->LE) {
2716 $message = substr($message, 0, -$lelen);
2717 }
2718
2719 //Split message into lines
2720 $lines = explode($this->LE, $message);
2721 //Message will be rebuilt in here
2722 $message = '';
2723 foreach ($lines as $line) {
2724 $words = explode(' ', $line);
2725 $buf = '';
2726 $firstword = true;
2727 foreach ($words as $word) {
2728 if ($qp_mode and (strlen($word) > $length)) {
2729 $space_left = $length - strlen($buf) - $crlflen;
2730 if (!$firstword) {
2731 if ($space_left > 20) {
2732 $len = $space_left;
2733 if ($is_utf8) {
2734 $len = $this->utf8CharBoundary($word, $len);
2735 } elseif (substr($word, $len - 1, 1) == '=') {
2736 $len--;
2737 } elseif (substr($word, $len - 2, 1) == '=') {
2738 $len -= 2;
2739 }
2740 $part = substr($word, 0, $len);
2741 $word = substr($word, $len);
2742 $buf .= ' ' . $part;
2743 $message .= $buf . sprintf('=%s', self::CRLF);
2744 } else {
2745 $message .= $buf . $soft_break;
2746 }
2747 $buf = '';
2748 }
2749 while (strlen($word) > 0) {
2750 if ($length <= 0) {
2751 break;
2752 }
2753 $len = $length;
2754 if ($is_utf8) {
2755 $len = $this->utf8CharBoundary($word, $len);
2756 } elseif (substr($word, $len - 1, 1) == '=') {
2757 $len--;
2758 } elseif (substr($word, $len - 2, 1) == '=') {
2759 $len -= 2;
2760 }
2761 $part = substr($word, 0, $len);
2762 $word = substr($word, $len);
2763
2764 if (strlen($word) > 0) {
2765 $message .= $part . sprintf('=%s', self::CRLF);
2766 } else {
2767 $buf = $part;
2768 }
2769 }
2770 } else {
2771 $buf_o = $buf;
2772 if (!$firstword) {
2773 $buf .= ' ';
2774 }
2775 $buf .= $word;
2776
2777 if (strlen($buf) > $length and $buf_o != '') {
2778 $message .= $buf_o . $soft_break;
2779 $buf = $word;
2780 }
2781 }
2782 $firstword = false;
2783 }
2784 $message .= $buf . self::CRLF;
2785 }
2786
2787 return $message;
2788 }
2789
2790 /**
2791 * Find the last character boundary prior to $maxLength in a utf-8
2792 * quoted-printable encoded string.
2793 * Original written by Colin Brown.
2794 * @access public
2795 * @param string $encodedText utf-8 QP text
2796 * @param integer $maxLength Find the last character boundary prior to this length
2797 * @return integer
2798 */
2799 public function utf8CharBoundary($encodedText, $maxLength)
2800 {
2801 $foundSplitPos = false;
2802 $lookBack = 3;
2803 while (!$foundSplitPos) {
2804 $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
2805 $encodedCharPos = strpos($lastChunk, '=');
2806 if (false !== $encodedCharPos) {
2807 // Found start of encoded character byte within $lookBack block.
2808 // Check the encoded byte value (the 2 chars after the '=')
2809 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
2810 $dec = hexdec($hex);
2811 if ($dec < 128) {
2812 // Single byte character.
2813 // If the encoded char was found at pos 0, it will fit
2814 // otherwise reduce maxLength to start of the encoded char
2815 if ($encodedCharPos > 0) {
2816 $maxLength = $maxLength - ($lookBack - $encodedCharPos);
2817 }
2818 $foundSplitPos = true;
2819 } elseif ($dec >= 192) {
2820 // First byte of a multi byte character
2821 // Reduce maxLength to split at start of character
2822 $maxLength = $maxLength - ($lookBack - $encodedCharPos);
2823 $foundSplitPos = true;
2824 } elseif ($dec < 192) {
2825 // Middle byte of a multi byte character, look further back
2826 $lookBack += 3;
2827 }
2828 } else {
2829 // No encoded character found
2830 $foundSplitPos = true;
2831 }
2832 }
2833 return $maxLength;
2834 }
2835
2836 /**
2837 * Apply word wrapping to the message body.
2838 * Wraps the message body to the number of chars set in the WordWrap property.
2839 * You should only do this to plain-text bodies as wrapping HTML tags may break them.
2840 * This is called automatically by createBody(), so you don't need to call it yourself.
2841 * @access public
2842 * @return void
2843 */
2844 public function setWordWrap()
2845 {
2846 if ($this->WordWrap < 1) {
2847 return;
2848 }
2849
2850 switch ($this->message_type) {
2851 case 'alt':
2852 case 'alt_inline':
2853 case 'alt_attach':
2854 case 'alt_inline_attach':
2855 $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
2856 break;
2857 default:
2858 $this->Body = $this->wrapText($this->Body, $this->WordWrap);
2859 break;
2860 }
2861 }
2862
2863 /**
2864 * Assemble message headers.
2865 * @access public
2866 * @return string The assembled headers
2867 */
2868 public function createHeader()
2869 {
2870 $result = '';
2871
2872 if ($this->MessageDate == '') {
2873 $this->MessageDate = self::rfcDate();
2874 }
2875 $result .= $this->headerLine('Date', $this->MessageDate);
2876
2877
2878 // To be created automatically by mail()
2879 if ($this->SingleTo) {
2880 if ($this->Mailer != 'mail') {
2881 foreach ($this->to as $toaddr) {
2882 $this->SingleToArray[] = $this->addrFormat($toaddr);
2883 }
2884 }
2885 } else {
2886 if (count($this->to) > 0) {
2887 if ($this->Mailer != 'mail') {
2888 $result .= $this->addrAppend('To', $this->to);
2889 }
2890 } elseif (count($this->cc) == 0) {
2891 $result .= $this->headerLine('To', 'undisclosed-recipients:;');
2892 }
2893 }
2894
2895 $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
2896
2897 // sendmail and mail() extract Cc from the header before sending
2898 if (count($this->cc) > 0) {
2899 $result .= $this->addrAppend('Cc', $this->cc);
2900 }
2901
2902 // sendmail and mail() extract Bcc from the header before sending
2903 if ((
2904 $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
2905 )
2906 and count($this->bcc) > 0
2907 ) {
2908 $result .= $this->addrAppend('Bcc', $this->bcc);
2909 }
2910
2911 if (count($this->ReplyTo) > 0) {
2912 $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
2913 }
2914
2915 // mail() sets the subject itself
2916 if ($this->Mailer != 'mail') {
2917 $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
2918 }
2919
2920 if ($this->MessageID != '') {
2921 $this->lastMessageID = $this->MessageID;
2922 } else {
2923 $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->ServerHostname());
2924 }
2925 $result .= $this->headerLine('Message-ID', $this->lastMessageID);
2926 $result .= $this->headerLine('X-Priority', $this->Priority);
2927 if ($this->XMailer == '') {
2928 $result .= $this->headerLine(
2929 'X-Mailer',
2930 'PHPMailer ' . $this->Version . ' (https://paypal.com)'
2931 );
2932 } else {
2933 $myXmailer = trim($this->XMailer);
2934 if ($myXmailer) {
2935 $result .= $this->headerLine('X-Mailer', $myXmailer);
2936 }
2937 }
2938
2939 if ($this->ConfirmReadingTo != '') {
2940 $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
2941 }
2942
2943 // Add custom headers
2944 foreach ($this->CustomHeader as $header) {
2945 $result .= $this->headerLine(
2946 trim($header[0]),
2947 $this->encodeHeader(trim($header[1]))
2948 );
2949 }
2950 if (!$this->sign_key_file) {
2951 $result .= $this->headerLine('MIME-Version', '1.0');
2952 $result .= $this->getMailMIME();
2953 }
2954
2955 return $result;
2956 }
2957
2958 /**
2959 * Get the message MIME type headers.
2960 * @access public
2961 * @return string
2962 */
2963 public function getMailMIME()
2964 {
2965 $result = '';
2966 $ismultipart = true;
2967 switch ($this->message_type) {
2968 case 'inline':
2969 $result .= $this->headerLine('Content-Type', 'multipart/related;');
2970 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2971 break;
2972 case 'attach':
2973 case 'inline_attach':
2974 case 'alt_attach':
2975 case 'alt_inline_attach':
2976 $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
2977 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2978 break;
2979 case 'alt':
2980 case 'alt_inline':
2981 $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
2982 $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
2983 break;
2984 default:
2985 // Catches case 'plain': and case '':
2986 $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
2987 $ismultipart = false;
2988 break;
2989 }
2990 // RFC1341 part 5 says 7bit is assumed if not specified
2991 if ($this->Encoding != '7bit') {
2992 // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
2993 if ($ismultipart) {
2994 if ($this->Encoding == '8bit') {
2995 $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
2996 }
2997 // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
2998 } else {
2999 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
3000 }
3001 }
3002
3003 if ($this->Mailer != 'mail') {
3004 $result .= $this->LE;
3005 }
3006
3007 return $result;
3008 }
3009
3010 /**
3011 * Returns the whole MIME message.
3012 * Includes complete headers and body.
3013 * Only valid post preSend().
3014 * @see PHPMailer::preSend()
3015 * @access public
3016 * @return string
3017 */
3018 public function getSentMIMEMessage()
3019 {
3020 return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
3021 }
3022
3023 /**
3024 * Assemble the message body.
3025 * Returns an empty string on failure.
3026 * @access public
3027 * @throws phpmailerException
3028 * @return string The assembled message body
3029 */
3030 public function createBody()
3031 {
3032 $body = '';
3033 //Create unique IDs and preset boundaries
3034 $this->uniqueid = md5(uniqid(time()));
3035 $this->boundary[1] = 'b1_' . $this->uniqueid;
3036 $this->boundary[2] = 'b2_' . $this->uniqueid;
3037 $this->boundary[3] = 'b3_' . $this->uniqueid;
3038
3039 if ($this->sign_key_file) {
3040 $body .= $this->getMailMIME() . $this->LE;
3041 }
3042
3043 $this->setWordWrap();
3044
3045 $bodyEncoding = $this->Encoding;
3046 $bodyCharSet = $this->CharSet;
3047 //Can we do a 7-bit downgrade?
3048 if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
3049 $bodyEncoding = '7bit';
3050 $bodyCharSet = 'us-ascii';
3051 }
3052 //If lines are too long, and we're not already using an encoding that will shorten them,
3053 //change to quoted-printable transfer encoding
3054 if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
3055 $this->Encoding = 'quoted-printable';
3056 $bodyEncoding = 'quoted-printable';
3057 }
3058
3059 $altBodyEncoding = $this->Encoding;
3060 $altBodyCharSet = $this->CharSet;
3061 //Can we do a 7-bit downgrade?
3062 if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
3063 $altBodyEncoding = '7bit';
3064 $altBodyCharSet = 'us-ascii';
3065 }
3066 //If lines are too long, change to quoted-printable transfer encoding
3067 if (self::hasLineLongerThanMax($this->AltBody)) {
3068 $altBodyEncoding = 'quoted-printable';
3069 }
3070 //Use this as a preamble in all multipart message types
3071 $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
3072 switch ($this->message_type) {
3073 case 'inline':
3074 $body .= $mimepre;
3075 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
3076 $body .= $this->encodeString($this->Body, $bodyEncoding);
3077 $body .= $this->LE . $this->LE;
3078 $body .= $this->attachAll('inline', $this->boundary[1]);
3079 break;
3080 case 'attach':
3081 $body .= $mimepre;
3082 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
3083 $body .= $this->encodeString($this->Body, $bodyEncoding);
3084 $body .= $this->LE . $this->LE;
3085 $body .= $this->attachAll('attachment', $this->boundary[1]);
3086 break;
3087 case 'inline_attach':
3088 $body .= $mimepre;
3089 $body .= $this->textLine('--' . $this->boundary[1]);
3090 $body .= $this->headerLine('Content-Type', 'multipart/related;');
3091 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
3092 $body .= $this->LE;
3093 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
3094 $body .= $this->encodeString($this->Body, $bodyEncoding);
3095 $body .= $this->LE . $this->LE;
3096 $body .= $this->attachAll('inline', $this->boundary[2]);
3097 $body .= $this->LE;
3098 $body .= $this->attachAll('attachment', $this->boundary[1]);
3099 break;
3100 case 'alt':
3101 $body .= $mimepre;
3102 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
3103 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
3104 $body .= $this->LE . $this->LE;
3105 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
3106 $body .= $this->encodeString($this->Body, $bodyEncoding);
3107 $body .= $this->LE . $this->LE;
3108 if (!empty($this->Ical)) {
3109 $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
3110 $body .= $this->encodeString($this->Ical, $this->Encoding);
3111 $body .= $this->LE . $this->LE;
3112 }
3113 $body .= $this->endBoundary($this->boundary[1]);
3114 break;
3115 case 'alt_inline':
3116 $body .= $mimepre;
3117 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
3118 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
3119 $body .= $this->LE . $this->LE;
3120 $body .= $this->textLine('--' . $this->boundary[1]);
3121 $body .= $this->headerLine('Content-Type', 'multipart/related;');
3122 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
3123 $body .= $this->LE;
3124 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
3125 $body .= $this->encodeString($this->Body, $bodyEncoding);
3126 $body .= $this->LE . $this->LE;
3127 $body .= $this->attachAll('inline', $this->boundary[2]);
3128 $body .= $this->LE;
3129 $body .= $this->endBoundary($this->boundary[1]);
3130 break;
3131 case 'alt_attach':
3132 $body .= $mimepre;
3133 $body .= $this->textLine('--' . $this->boundary[1]);
3134 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
3135 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
3136 $body .= $this->LE;
3137 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
3138 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
3139 $body .= $this->LE . $this->LE;
3140 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
3141 $body .= $this->encodeString($this->Body, $bodyEncoding);
3142 $body .= $this->LE . $this->LE;
3143 $body .= $this->endBoundary($this->boundary[2]);
3144 $body .= $this->LE;
3145 $body .= $this->attachAll('attachment', $this->boundary[1]);
3146 break;
3147 case 'alt_inline_attach':
3148 $body .= $mimepre;
3149 $body .= $this->textLine('--' . $this->boundary[1]);
3150 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
3151 $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
3152 $body .= $this->LE;
3153 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
3154 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
3155 $body .= $this->LE . $this->LE;
3156 $body .= $this->textLine('--' . $this->boundary[2]);
3157 $body .= $this->headerLine('Content-Type', 'multipart/related;');
3158 $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
3159 $body .= $this->LE;
3160 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
3161 $body .= $this->encodeString($this->Body, $bodyEncoding);
3162 $body .= $this->LE . $this->LE;
3163 $body .= $this->attachAll('inline', $this->boundary[3]);
3164 $body .= $this->LE;
3165 $body .= $this->endBoundary($this->boundary[2]);
3166 $body .= $this->LE;
3167 $body .= $this->attachAll('attachment', $this->boundary[1]);
3168 break;
3169 default:
3170 // catch case 'plain' and case ''
3171 $body .= $this->encodeString($this->Body, $bodyEncoding);
3172 break;
3173 }
3174
3175 if ($this->isError()) {
3176 $body = '';
3177 } elseif ($this->sign_key_file) {
3178 try {
3179 if (!defined('PKCS7_TEXT')) {
3180 throw new phpmailerException($this->lang('extension_missing') . 'openssl');
3181 }
3182 // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
3183 $file = tempnam(sys_get_temp_dir(), 'mail');
3184 if (false === file_put_contents($file, $body)) {
3185 throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
3186 }
3187 $signed = tempnam(sys_get_temp_dir(), 'signed');
3188 //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
3189 if (empty($this->sign_extracerts_file)) {
3190 $sign = @openssl_pkcs7_sign(
3191 $file,
3192 $signed,
3193 'file://' . realpath($this->sign_cert_file),
3194 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
3195 null
3196 );
3197 } else {
3198 $sign = @openssl_pkcs7_sign(
3199 $file,
3200 $signed,
3201 'file://' . realpath($this->sign_cert_file),
3202 array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
3203 null,
3204 PKCS7_DETACHED,
3205 $this->sign_extracerts_file
3206 );
3207 }
3208 if ($sign) {
3209 @unlink($file);
3210 $body = file_get_contents($signed);
3211 @unlink($signed);
3212 //The message returned by openssl contains both headers and body, so need to split them up
3213 $parts = explode("\n\n", $body, 2);
3214 $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
3215 $body = $parts[1];
3216 } else {
3217 @unlink($file);
3218 @unlink($signed);
3219 throw new phpmailerException($this->lang('signing') . openssl_error_string());
3220 }
3221 } catch (phpmailerException $exc) {
3222 $body = '';
3223 if ($this->exceptions) {
3224 throw $exc;
3225 }
3226 }
3227 }
3228 return $body;
3229 }
3230
3231 /**
3232 * Return the start of a message boundary.
3233 * @access protected
3234 * @param string $boundary
3235 * @param string $charSet
3236 * @param string $contentType
3237 * @param string $encoding
3238 * @return string
3239 */
3240 protected function getBoundary($boundary, $charSet, $contentType, $encoding)
3241 {
3242 $result = '';
3243 if ($charSet == '') {
3244 $charSet = $this->CharSet;
3245 }
3246 if ($contentType == '') {
3247 $contentType = $this->ContentType;
3248 }
3249 if ($encoding == '') {
3250 $encoding = $this->Encoding;
3251 }
3252 $result .= $this->textLine('--' . $boundary);
3253 $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
3254 $result .= $this->LE;
3255 // RFC1341 part 5 says 7bit is assumed if not specified
3256 if ($encoding != '7bit') {
3257 $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
3258 }
3259 $result .= $this->LE;
3260
3261 return $result;
3262 }
3263
3264 /**
3265 * Return the end of a message boundary.
3266 * @access protected
3267 * @param string $boundary
3268 * @return string
3269 */
3270 protected function endBoundary($boundary)
3271 {
3272 return $this->LE . '--' . $boundary . '--' . $this->LE;
3273 }
3274
3275 /**
3276 * Set the message type.
3277 * PHPMailer only supports some preset message types,
3278 * not arbitrary MIME structures.
3279 * @access protected
3280 * @return void
3281 */
3282 protected function setMessageType()
3283 {
3284 $type = array();
3285 if ($this->alternativeExists()) {
3286 $type[] = 'alt';
3287 }
3288 if ($this->inlineImageExists()) {
3289 $type[] = 'inline';
3290 }
3291 if ($this->attachmentExists()) {
3292 $type[] = 'attach';
3293 }
3294 $this->message_type = implode('_', $type);
3295 if ($this->message_type == '') {
3296 $this->message_type = 'plain';
3297 }
3298 }
3299
3300 /**
3301 * Format a header line.
3302 * @access public
3303 * @param string $name
3304 * @param string $value
3305 * @return string
3306 */
3307 public function headerLine($name, $value)
3308 {
3309 return $name . ': ' . $value . $this->LE;
3310 }
3311
3312 /**
3313 * Return a formatted mail line.
3314 * @access public
3315 * @param string $value
3316 * @return string
3317 */
3318 public function textLine($value)
3319 {
3320 return $value . $this->LE;
3321 }
3322
3323 /**
3324 * Add an attachment from a path on the filesystem.
3325 * Returns false if the file could not be found or read.
3326 * @param string $path Path to the attachment.
3327 * @param string $name Overrides the attachment name.
3328 * @param string $encoding File encoding (see $Encoding).
3329 * @param string $type File extension (MIME) type.
3330 * @param string $disposition Disposition to use
3331 * @throws phpmailerException
3332 * @return boolean
3333 */
3334 public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
3335 {
3336 try {
3337 if (!@is_file($path)) {
3338 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
3339 }
3340
3341 // If a MIME type is not specified, try to work it out from the file name
3342 if ($type == '') {
3343 $type = self::filenameToType($path);
3344 }
3345
3346 $filename = basename($path);
3347 if ($name == '') {
3348 $name = $filename;
3349 }
3350
3351 $this->attachment[] = array(
3352 0 => $path,
3353 1 => $filename,
3354 2 => $name,
3355 3 => $encoding,
3356 4 => $type,
3357 5 => false, // isStringAttachment
3358 6 => $disposition,
3359 7 => 0
3360 );
3361
3362 } catch (phpmailerException $exc) {
3363 $this->setError($exc->getMessage());
3364 $this->edebug($exc->getMessage());
3365 if ($this->exceptions) {
3366 throw $exc;
3367 }
3368 return false;
3369 }
3370 return true;
3371 }
3372
3373 /**
3374 * Return the array of attachments.
3375 * @return array
3376 */
3377 public function getAttachments()
3378 {
3379 return $this->attachment;
3380 }
3381
3382 /**
3383 * Attach all file, string, and binary attachments to the message.
3384 * Returns an empty string on failure.
3385 * @access protected
3386 * @param string $disposition_type
3387 * @param string $boundary
3388 * @return string
3389 */
3390 protected function attachAll($disposition_type, $boundary)
3391 {
3392 // Return text of body
3393 $mime = array();
3394 $cidUniq = array();
3395 $incl = array();
3396
3397 // Add all attachments
3398 foreach ($this->attachment as $attachment) {
3399 // Check if it is a valid disposition_filter
3400 if ($attachment[6] == $disposition_type) {
3401 // Check for string attachment
3402 $string = '';
3403 $path = '';
3404 $bString = $attachment[5];
3405 if ($bString) {
3406 $string = $attachment[0];
3407 } else {
3408 $path = $attachment[0];
3409 }
3410
3411 $inclhash = md5(serialize($attachment));
3412 if (in_array($inclhash, $incl)) {
3413 continue;
3414 }
3415 $incl[] = $inclhash;
3416 $name = $attachment[2];
3417 $encoding = $attachment[3];
3418 $type = $attachment[4];
3419 $disposition = $attachment[6];
3420 $cid = $attachment[7];
3421 if ($disposition == 'inline' && isset($cidUniq[$cid])) {
3422 continue;
3423 }
3424 $cidUniq[$cid] = true;
3425
3426 $mime[] = sprintf('--%s%s', $boundary, $this->LE);
3427 $mime[] = sprintf(
3428 'Content-Type: %s; name="%s"%s',
3429 $type,
3430 $this->encodeHeader($this->secureHeader($name)),
3431 $this->LE
3432 );
3433 // RFC1341 part 5 says 7bit is assumed if not specified
3434 if ($encoding != '7bit') {
3435 $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
3436 }
3437
3438 if ($disposition == 'inline') {
3439 $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
3440 }
3441
3442 // If a filename contains any of these chars, it should be quoted,
3443 // but not otherwise: RFC2183 & RFC2045 5.1
3444 // Fixes a warning in IETF's msglint MIME checker
3445 // Allow for bypassing the Content-Disposition header totally
3446 if (!(empty($disposition))) {
3447 $encoded_name = $this->encodeHeader($this->secureHeader($name));
3448 if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
3449 $mime[] = sprintf(
3450 'Content-Disposition: %s; filename="%s"%s',
3451 $disposition,
3452 $encoded_name,
3453 $this->LE . $this->LE
3454 );
3455 } else {
3456 $mime[] = sprintf(
3457 'Content-Disposition: %s; filename=%s%s',
3458 $disposition,
3459 $encoded_name,
3460 $this->LE . $this->LE
3461 );
3462 }
3463 } else {
3464 $mime[] = $this->LE;
3465 }
3466
3467 // Encode as string attachment
3468 if ($bString) {
3469 $mime[] = $this->encodeString($string, $encoding);
3470 if ($this->isError()) {
3471 return '';
3472 }
3473 $mime[] = $this->LE . $this->LE;
3474 } else {
3475 $mime[] = $this->encodeFile($path, $encoding);
3476 if ($this->isError()) {
3477 return '';
3478 }
3479 $mime[] = $this->LE . $this->LE;
3480 }
3481 }
3482 }
3483
3484 $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
3485
3486 return implode('', $mime);
3487 }
3488
3489 /**
3490 * Encode a file attachment in requested format.
3491 * Returns an empty string on failure.
3492 * @param string $path The full path to the file
3493 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
3494 * @throws phpmailerException
3495 * @see EncodeFile(encodeFile
3496 * @access protected
3497 * @return string
3498 */
3499 protected function encodeFile($path, $encoding = 'base64')
3500 {
3501 try {
3502 if (!is_readable($path)) {
3503 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
3504 }
3505 $magic_quotes = get_magic_quotes_runtime();
3506 if ($magic_quotes) {
3507 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
3508 set_magic_quotes_runtime(false);
3509 } else {
3510 //Doesn't exist in PHP 5.4, but we don't need to check because
3511 //get_magic_quotes_runtime always returns false in 5.4+
3512 //so it will never get here
3513 ini_set('magic_quotes_runtime', false);
3514 }
3515 }
3516 $file_buffer = file_get_contents($path);
3517 $file_buffer = $this->encodeString($file_buffer, $encoding);
3518 if ($magic_quotes) {
3519 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
3520 set_magic_quotes_runtime($magic_quotes);
3521 } else {
3522 ini_set('magic_quotes_runtime', $magic_quotes);
3523 }
3524 }
3525 return $file_buffer;
3526 } catch (Exception $exc) {
3527 $this->setError($exc->getMessage());
3528 return '';
3529 }
3530 }
3531
3532 /**
3533 * Encode a string in requested format.
3534 * Returns an empty string on failure.
3535 * @param string $str The text to encode
3536 * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
3537 * @access public
3538 * @return string
3539 */
3540 public function encodeString($str, $encoding = 'base64')
3541 {
3542 $encoded = '';
3543 switch (strtolower($encoding)) {
3544 case 'base64':
3545 $encoded = chunk_split(base64_encode($str), 76, $this->LE);
3546 break;
3547 case '7bit':
3548 case '8bit':
3549 $encoded = $this->fixEOL($str);
3550 // Make sure it ends with a line break
3551 if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
3552 $encoded .= $this->LE;
3553 }
3554 break;
3555 case 'binary':
3556 $encoded = $str;
3557 break;
3558 case 'quoted-printable':
3559 $encoded = $this->encodeQP($str);
3560 break;
3561 default:
3562 $this->setError($this->lang('encoding') . $encoding);
3563 break;
3564 }
3565 return $encoded;
3566 }
3567
3568 /**
3569 * Encode a header string optimally.
3570 * Picks shortest of Q, B, quoted-printable or none.
3571 * @access public
3572 * @param string $str
3573 * @param string $position
3574 * @return string
3575 */
3576 public function encodeHeader($str, $position = 'text')
3577 {
3578 $matchcount = 0;
3579 switch (strtolower($position)) {
3580 case 'phrase':
3581 if (!preg_match('/[\200-\377]/', $str)) {
3582 // Can't use addslashes as we don't know the value of magic_quotes_sybase
3583 $encoded = addcslashes($str, "\0..\37\177\\\"");
3584 if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
3585 return ($encoded);
3586 } else {
3587 return ("\"$encoded\"");
3588 }
3589 }
3590 $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
3591 break;
3592 /** @noinspection PhpMissingBreakStatementInspection */
3593 case 'comment':
3594 $matchcount = preg_match_all('/[()"]/', $str, $matches);
3595 // Intentional fall-through
3596 case 'text':
3597 default:
3598 $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
3599 break;
3600 }
3601
3602 //There are no chars that need encoding
3603 if ($matchcount == 0) {
3604 return ($str);
3605 }
3606
3607 $maxlen = 75 - 7 - strlen($this->CharSet);
3608 // Try to select the encoding which should produce the shortest output
3609 if ($matchcount > strlen($str) / 3) {
3610 // More than a third of the content will need encoding, so B encoding will be most efficient
3611 $encoding = 'B';
3612 if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
3613 // Use a custom function which correctly encodes and wraps long
3614 // multibyte strings without breaking lines within a character
3615 $encoded = $this->base64EncodeWrapMB($str, "\n");
3616 } else {
3617 $encoded = base64_encode($str);
3618 $maxlen -= $maxlen % 4;
3619 $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
3620 }
3621 } else {
3622 $encoding = 'Q';
3623 $encoded = $this->encodeQ($str, $position);
3624 $encoded = $this->wrapText($encoded, $maxlen, true);
3625 $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
3626 }
3627
3628 $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
3629 $encoded = trim(str_replace("\n", $this->LE, $encoded));
3630
3631 return $encoded;
3632 }
3633
3634 /**
3635 * Check if a string contains multi-byte characters.
3636 * @access public
3637 * @param string $str multi-byte text to wrap encode
3638 * @return boolean
3639 */
3640 public function hasMultiBytes($str)
3641 {
3642 if (function_exists('mb_strlen')) {
3643 return (strlen($str) > mb_strlen($str, $this->CharSet));
3644 } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
3645 return false;
3646 }
3647 }
3648
3649 /**
3650 * Does a string contain any 8-bit chars (in any charset)?
3651 * @param string $text
3652 * @return boolean
3653 */
3654 public function has8bitChars($text)
3655 {
3656 return (boolean)preg_match('/[\x80-\xFF]/', $text);
3657 }
3658
3659 /**
3660 * Encode and wrap long multibyte strings for mail headers
3661 * without breaking lines within a character.
3662 * Adapted from a function by paravoid
3663 * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
3664 * @access public
3665 * @param string $str multi-byte text to wrap encode
3666 * @param string $linebreak string to use as linefeed/end-of-line
3667 * @return string
3668 */
3669 public function base64EncodeWrapMB($str, $linebreak = null)
3670 {
3671 $start = '=?' . $this->CharSet . '?B?';
3672 $end = '?=';
3673 $encoded = '';
3674 if ($linebreak === null) {
3675 $linebreak = $this->LE;
3676 }
3677
3678 $mb_length = mb_strlen($str, $this->CharSet);
3679 // Each line must have length <= 75, including $start and $end
3680 $length = 75 - strlen($start) - strlen($end);
3681 // Average multi-byte ratio
3682 $ratio = $mb_length / strlen($str);
3683 // Base64 has a 4:3 ratio
3684 $avgLength = floor($length * $ratio * .75);
3685
3686 for ($i = 0; $i < $mb_length; $i += $offset) {
3687 $lookBack = 0;
3688 do {
3689 $offset = $avgLength - $lookBack;
3690 $chunk = mb_substr($str, $i, $offset, $this->CharSet);
3691 $chunk = base64_encode($chunk);
3692 $lookBack++;
3693 } while (strlen($chunk) > $length);
3694 $encoded .= $chunk . $linebreak;
3695 }
3696
3697 // Chomp the last linefeed
3698 $encoded = substr($encoded, 0, -strlen($linebreak));
3699 return $encoded;
3700 }
3701
3702 /**
3703 * Encode a string in quoted-printable format.
3704 * According to RFC2045 section 6.7.
3705 * @access public
3706 * @param string $string The text to encode
3707 * @param integer $line_max Number of chars allowed on a line before wrapping
3708 * @return string
3709 * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
3710 */
3711 public function encodeQP($string, $line_max = 76)
3712 {
3713 // Use native function if it's available (>= PHP5.3)
3714 if (function_exists('quoted_printable_encode')) {
3715 return $this->fixEOL(quoted_printable_encode($string));
3716 }
3717 // Fall back to a pure PHP implementation
3718 $string = str_replace(
3719 array('%20', '%0D%0A.', '%0D%0A', '%'),
3720 array(' ', "\r\n=2E", "\r\n", '='),
3721 rawurlencode($string)
3722 );
3723 $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
3724 return $this->fixEOL($string);
3725 }
3726
3727 /**
3728 * Backward compatibility wrapper for an old QP encoding function that was removed.
3729 * @see PHPMailer::encodeQP()
3730 * @access public
3731 * @param string $string
3732 * @param integer $line_max
3733 * @param boolean $space_conv
3734 * @return string
3735 * @deprecated Use encodeQP instead.
3736 */
3737 public function encodeQPphp(
3738 $string,
3739 $line_max = 76,
3740 /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
3741 ) {
3742 return $this->encodeQP($string, $line_max);
3743 }
3744
3745 /**
3746 * Encode a string using Q encoding.
3747 * @link http://tools.ietf.org/html/rfc2047
3748 * @param string $str the text to encode
3749 * @param string $position Where the text is going to be used, see the RFC for what that means
3750 * @access public
3751 * @return string
3752 */
3753 public function encodeQ($str, $position = 'text')
3754 {
3755 // There should not be any EOL in the string
3756 $pattern = '';
3757 $encoded = str_replace(array("\r", "\n"), '', $str);
3758 switch (strtolower($position)) {
3759 case 'phrase':
3760 // RFC 2047 section 5.3
3761 $pattern = '^A-Za-z0-9!*+\/ -';
3762 break;
3763 /** @noinspection PhpMissingBreakStatementInspection */
3764 case 'comment':
3765 // RFC 2047 section 5.2
3766 $pattern = '\(\)"';
3767 // intentional fall-through
3768 // for this reason we build the $pattern without including delimiters and []
3769 case 'text':
3770 default:
3771 // RFC 2047 section 5.1
3772 // Replace every high ascii, control, =, ? and _ characters
3773 $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
3774 break;
3775 }
3776 $matches = array();
3777 if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
3778 // If the string contains an '=', make sure it's the first thing we replace
3779 // so as to avoid double-encoding
3780 $eqkey = array_search('=', $matches[0]);
3781 if (false !== $eqkey) {
3782 unset($matches[0][$eqkey]);
3783 array_unshift($matches[0], '=');
3784 }
3785 foreach (array_unique($matches[0]) as $char) {
3786 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
3787 }
3788 }
3789 // Replace every spaces to _ (more readable than =20)
3790 return str_replace(' ', '_', $encoded);
3791 }
3792
3793
3794 /**
3795 * Add a string or binary attachment (non-filesystem).
3796 * This method can be used to attach ascii or binary data,
3797 * such as a BLOB record from a database.
3798 * @param string $string String attachment data.
3799 * @param string $filename Name of the attachment.
3800 * @param string $encoding File encoding (see $Encoding).
3801 * @param string $type File extension (MIME) type.
3802 * @param string $disposition Disposition to use
3803 * @return void
3804 */
3805 public function addStringAttachment(
3806 $string,
3807 $filename,
3808 $encoding = 'base64',
3809 $type = '',
3810 $disposition = 'attachment'
3811 ) {
3812 // If a MIME type is not specified, try to work it out from the file name
3813 if ($type == '') {
3814 $type = self::filenameToType($filename);
3815 }
3816 // Append to $attachment array
3817 $this->attachment[] = array(
3818 0 => $string,
3819 1 => $filename,
3820 2 => basename($filename),
3821 3 => $encoding,
3822 4 => $type,
3823 5 => true, // isStringAttachment
3824 6 => $disposition,
3825 7 => 0
3826 );
3827 }
3828
3829 /**
3830 * Add an embedded (inline) attachment from a file.
3831 * This can include images, sounds, and just about any other document type.
3832 * These differ from 'regular' attachments in that they are intended to be
3833 * displayed inline with the message, not just attached for download.
3834 * This is used in HTML messages that embed the images
3835 * the HTML refers to using the $cid value.
3836 * @param string $path Path to the attachment.
3837 * @param string $cid Content ID of the attachment; Use this to reference
3838 * the content when using an embedded image in HTML.
3839 * @param string $name Overrides the attachment name.
3840 * @param string $encoding File encoding (see $Encoding).
3841 * @param string $type File MIME type.
3842 * @param string $disposition Disposition to use
3843 * @return boolean True on successfully adding an attachment
3844 */
3845 public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
3846 {
3847 if (!@is_file($path)) {
3848 $this->setError($this->lang('file_access') . $path);
3849 return false;
3850 }
3851
3852 // If a MIME type is not specified, try to work it out from the file name
3853 if ($type == '') {
3854 $type = self::filenameToType($path);
3855 }
3856
3857 $filename = basename($path);
3858 if ($name == '') {
3859 $name = $filename;
3860 }
3861
3862 // Append to $attachment array
3863 $this->attachment[] = array(
3864 0 => $path,
3865 1 => $filename,
3866 2 => $name,
3867 3 => $encoding,
3868 4 => $type,
3869 5 => false, // isStringAttachment
3870 6 => $disposition,
3871 7 => $cid
3872 );
3873 return true;
3874 }
3875
3876 /**
3877 * Add an embedded stringified attachment.
3878 * This can include images, sounds, and just about any other document type.
3879 * Be sure to set the $type to an image type for images:
3880 * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
3881 * @param string $string The attachment binary data.
3882 * @param string $cid Content ID of the attachment; Use this to reference
3883 * the content when using an embedded image in HTML.
3884 * @param string $name
3885 * @param string $encoding File encoding (see $Encoding).
3886 * @param string $type MIME type.
3887 * @param string $disposition Disposition to use
3888 * @return boolean True on successfully adding an attachment
3889 */
3890 public function addStringEmbeddedImage(
3891 $string,
3892 $cid,
3893 $name = '',
3894 $encoding = 'base64',
3895 $type = '',
3896 $disposition = 'inline'
3897 ) {
3898 // If a MIME type is not specified, try to work it out from the name
3899 if ($type == '') {
3900 $type = self::filenameToType($name);
3901 }
3902
3903 // Append to $attachment array
3904 $this->attachment[] = array(
3905 0 => $string,
3906 1 => $name,
3907 2 => $name,
3908 3 => $encoding,
3909 4 => $type,
3910 5 => true, // isStringAttachment
3911 6 => $disposition,
3912 7 => $cid
3913 );
3914 return true;
3915 }
3916
3917 /**
3918 * Check if an inline attachment is present.
3919 * @access public
3920 * @return boolean
3921 */
3922 public function inlineImageExists()
3923 {
3924 foreach ($this->attachment as $attachment) {
3925 if ($attachment[6] == 'inline') {
3926 return true;
3927 }
3928 }
3929 return false;
3930 }
3931
3932 /**
3933 * Check if an attachment (non-inline) is present.
3934 * @return boolean
3935 */
3936 public function attachmentExists()
3937 {
3938 foreach ($this->attachment as $attachment) {
3939 if ($attachment[6] == 'attachment') {
3940 return true;
3941 }
3942 }
3943 return false;
3944 }
3945
3946 /**
3947 * Check if this message has an alternative body set.
3948 * @return boolean
3949 */
3950 public function alternativeExists()
3951 {
3952 return !empty($this->AltBody);
3953 }
3954
3955 /**
3956 * Clear all To recipients.
3957 * @return void
3958 */
3959 public function clearAddresses()
3960 {
3961 foreach ($this->to as $to) {
3962 unset($this->all_recipients[strtolower($to[0])]);
3963 }
3964 $this->to = array();
3965 }
3966
3967 /**
3968 * Clear all CC recipients.
3969 * @return void
3970 */
3971 public function clearCCs()
3972 {
3973 foreach ($this->cc as $cc) {
3974 unset($this->all_recipients[strtolower($cc[0])]);
3975 }
3976 $this->cc = array();
3977 }
3978
3979 /**
3980 * Clear all BCC recipients.
3981 * @return void
3982 */
3983 public function clearBCCs()
3984 {
3985 foreach ($this->bcc as $bcc) {
3986 unset($this->all_recipients[strtolower($bcc[0])]);
3987 }
3988 $this->bcc = array();
3989 }
3990
3991 /**
3992 * Clear all ReplyTo recipients.
3993 * @return void
3994 */
3995 public function clearReplyTos()
3996 {
3997 $this->ReplyTo = array();
3998 }
3999
4000 /**
4001 * Clear all recipient types.
4002 * @return void
4003 */
4004 public function clearAllRecipients()
4005 {
4006 $this->to = array();
4007 $this->cc = array();
4008 $this->bcc = array();
4009 $this->all_recipients = array();
4010 }
4011
4012 /**
4013 * Clear all filesystem, string, and binary attachments.
4014 * @return void
4015 */
4016 public function clearAttachments()
4017 {
4018 $this->attachment = array();
4019 }
4020
4021 /**
4022 * Clear all custom headers.
4023 * @return void
4024 */
4025 public function clearCustomHeaders()
4026 {
4027 $this->CustomHeader = array();
4028 }
4029
4030 /**
4031 * Add an error message to the error container.
4032 * @access protected
4033 * @param string $msg
4034 * @return void
4035 */
4036 protected function setError($msg)
4037 {
4038 $this->error_count++;
4039 if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
4040 $lasterror = $this->smtp->getError();
4041 if (!empty($lasterror['error'])) {
4042 $msg .= $this->lang('smtp_error') . $lasterror['error'];
4043 if (!empty($lasterror['detail'])) {
4044 $msg .= ' Detail: '. $lasterror['detail'];
4045 }
4046 if (!empty($lasterror['smtp_code'])) {
4047 $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
4048 }
4049 if (!empty($lasterror['smtp_code_ex'])) {
4050 $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
4051 }
4052 }
4053 }
4054 $this->ErrorInfo = $msg;
4055 }
4056
4057 /**
4058 * Return an RFC 822 formatted date.
4059 * @access public
4060 * @return string
4061 * @static
4062 */
4063 public static function rfcDate()
4064 {
4065 // Set the time zone to whatever the default is to avoid 500 errors
4066 // Will default to UTC if it's not set properly in php.ini
4067 date_default_timezone_set(@date_default_timezone_get());
4068 return date('D, j M Y H:i:s O');
4069 }
4070
4071 /**
4072 * Get the server hostname.
4073 * Returns 'localhost.localdomain' if unknown.
4074 * @access protected
4075 * @return string
4076 */
4077 protected function serverHostname()
4078 {
4079 $result = 'localhost.localdomain';
4080 if (!empty($this->Hostname)) {
4081 $result = $this->Hostname;
4082 } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
4083 $result = $_SERVER['SERVER_NAME'];
4084 } elseif (function_exists('gethostname') && gethostname() !== false) {
4085 $result = gethostname();
4086 } elseif (php_uname('n') !== false) {
4087 $result = php_uname('n');
4088 }
4089 return $result;
4090 }
4091
4092 /**
4093 * Get an error message in the current language.
4094 * @access protected
4095 * @param string $key
4096 * @return string
4097 */
4098 protected function lang($key)
4099 {
4100 if (count($this->language) < 1) {
4101 $this->setLanguage('en'); // set the default language
4102 }
4103
4104 if (array_key_exists($key, $this->language)) {
4105 if ($key == 'smtp_connect_failed') {
4106 //Include a link to troubleshooting docs on SMTP connection failure
4107 //this is by far the biggest cause of support questions
4108 //but it's usually not PHPMailer's fault.
4109 return $this->language[$key] . ' https://paypal.comwiki/Troubleshooting';
4110 }
4111 return $this->language[$key];
4112 } else {
4113 //Return the key as a fallback
4114 return $key;
4115 }
4116 }
4117
4118 /**
4119 * Check if an error occurred.
4120 * @access public
4121 * @return boolean True if an error did occur.
4122 */
4123 public function isError()
4124 {
4125 return ($this->error_count > 0);
4126 }
4127
4128 /**
4129 * Ensure consistent line endings in a string.
4130 * Changes every end of line from CRLF, CR or LF to $this->LE.
4131 * @access public
4132 * @param string $str String to fixEOL
4133 * @return string
4134 */
4135 public function fixEOL($str)
4136 {
4137 // Normalise to \n
4138 $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
4139 // Now convert LE as needed
4140 if ($this->LE !== "\n") {
4141 $nstr = str_replace("\n", $this->LE, $nstr);
4142 }
4143 return $nstr;
4144 }
4145
4146 /**
4147 * Add a custom header.
4148 * $name value can be overloaded to contain
4149 * both header name and value (name:value)
4150 * @access public
4151 * @param string $name Custom header name
4152 * @param string $value Header value
4153 * @return void
4154 */
4155 public function addCustomHeader($name, $value = null)
4156 {
4157 if ($value === null) {
4158 // Value passed in as name:value
4159 $this->CustomHeader[] = explode(':', $name, 2);
4160 } else {
4161 $this->CustomHeader[] = array($name, $value);
4162 }
4163 }
4164
4165 /**
4166 * Returns all custom headers
4167 *
4168 * @return array
4169 */
4170 public function getCustomHeaders()
4171 {
4172 return $this->CustomHeader;
4173 }
4174
4175 /**
4176 * Create a message from an HTML string.
4177 * Automatically makes modifications for inline images and backgrounds
4178 * and creates a plain-text version by converting the HTML.
4179 * Overwrites any existing values in $this->Body and $this->AltBody
4180 * @access public
4181 * @param string $message HTML message string
4182 * @param string $basedir baseline directory for path
4183 * @param boolean|callable $advanced Whether to use the internal HTML to text converter
4184 * or your own custom converter @see html2text()
4185 * @return string $message
4186 */
4187 public function msgHTML($message, $basedir = '', $advanced = false)
4188 {
4189 preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
4190 if (isset($images[2])) {
4191 foreach ($images[2] as $imgindex => $url) {
4192 // Convert data URIs into embedded images
4193 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
4194 $data = substr($url, strpos($url, ','));
4195 if ($match[2]) {
4196 $data = base64_decode($data);
4197 } else {
4198 $data = rawurldecode($data);
4199 }
4200 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
4201 if ($this->addStringEmbeddedImage($data, $cid, '', 'base64', $match[1])) {
4202 $message = str_replace(
4203 $images[0][$imgindex],
4204 $images[1][$imgindex] . '="cid:' . $cid . '"',
4205 $message
4206 );
4207 }
4208 } elseif (!preg_match('#^[A-z]+://#', $url)) {
4209 // Do not change urls for absolute images (thanks to corvuscorax)
4210 $filename = basename($url);
4211 $directory = dirname($url);
4212 if ($directory == '.') {
4213 $directory = '';
4214 }
4215 $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
4216 if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
4217 $basedir .= '/';
4218 }
4219 if (strlen($directory) > 1 && substr($directory, -1) != '/') {
4220 $directory .= '/';
4221 }
4222 if ($this->addEmbeddedImage(
4223 $basedir . $directory . $filename,
4224 $cid,
4225 $filename,
4226 'base64',
4227 self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
4228 )
4229 ) {
4230 $message = preg_replace(
4231 '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
4232 $images[1][$imgindex] . '="cid:' . $cid . '"',
4233 $message
4234 );
4235 }
4236 }
4237 }
4238 }
4239 $this->isHTML(true);
4240 // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
4241 $this->Body = $this->normalizeBreaks($message);
4242 $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
4243 if (empty($this->AltBody)) {
4244 $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
4245 self::CRLF . self::CRLF;
4246 }
4247 return $this->Body;
4248 }
4249
4250 /**
4251 * Convert an HTML string into plain text.
4252 * This is used by msgHTML().
4253 * Note - older versions of this function used a bundled advanced converter
4254 * which was been removed for license reasons in #232
4255 * Example usage:
4256 * <code>
4257 * // Use default conversion
4258 * $plain = $mail->html2text($html);
4259 * // Use your own custom converter
4260 * $plain = $mail->html2text($html, function($html) {
4261 * $converter = new MyHtml2text($html);
4262 * return $converter->get_text();
4263 * });
4264 * </code>
4265 * @param string $html The HTML text to convert
4266 * @param boolean|callable $advanced Any boolean value to use the internal converter,
4267 * or provide your own callable for custom conversion.
4268 * @return string
4269 */
4270 public function html2text($html, $advanced = false)
4271 {
4272 if (is_callable($advanced)) {
4273 return call_user_func($advanced, $html);
4274 }
4275 return html_entity_decode(
4276 trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
4277 ENT_QUOTES,
4278 $this->CharSet
4279 );
4280 }
4281
4282 /**
4283 * Get the MIME type for a file extension.
4284 * @param string $ext File extension
4285 * @access public
4286 * @return string MIME type of file.
4287 * @static
4288 */
4289 public static function _mime_types($ext = '')
4290 {
4291 $mimes = array(
4292 'xl' => 'application/excel',
4293 'js' => 'application/javascript',
4294 'hqx' => 'application/mac-binhex40',
4295 'cpt' => 'application/mac-compactpro',
4296 'bin' => 'application/macbinary',
4297 'doc' => 'application/msword',
4298 'word' => 'application/msword',
4299 'class' => 'application/octet-stream',
4300 'dll' => 'application/octet-stream',
4301 'dms' => 'application/octet-stream',
4302 'exe' => 'application/octet-stream',
4303 'lha' => 'application/octet-stream',
4304 'lzh' => 'application/octet-stream',
4305 'psd' => 'application/octet-stream',
4306 'sea' => 'application/octet-stream',
4307 'so' => 'application/octet-stream',
4308 'oda' => 'application/oda',
4309 'pdf' => 'application/pdf',
4310 'ai' => 'application/postscript',
4311 'eps' => 'application/postscript',
4312 'ps' => 'application/postscript',
4313 'smi' => 'application/smil',
4314 'smil' => 'application/smil',
4315 'mif' => 'application/vnd.mif',
4316 'xls' => 'application/vnd.ms-excel',
4317 'ppt' => 'application/vnd.ms-powerpoint',
4318 'wbxml' => 'application/vnd.wap.wbxml',
4319 'wmlc' => 'application/vnd.wap.wmlc',
4320 'dcr' => 'application/x-director',
4321 'dir' => 'application/x-director',
4322 'dxr' => 'application/x-director',
4323 'dvi' => 'application/x-dvi',
4324 'gtar' => 'application/x-gtar',
4325 'php3' => 'application/x-httpd-php',
4326 'php4' => 'application/x-httpd-php',
4327 'php' => 'application/x-httpd-php',
4328 'phtml' => 'application/x-httpd-php',
4329 'phps' => 'application/x-httpd-php-source',
4330 'swf' => 'application/x-shockwave-flash',
4331 'sit' => 'application/x-stuffit',
4332 'tar' => 'application/x-tar',
4333 'tgz' => 'application/x-tar',
4334 'xht' => 'application/xhtml+xml',
4335 'xhtml' => 'application/xhtml+xml',
4336 'zip' => 'application/zip',
4337 'mid' => 'audio/midi',
4338 'midi' => 'audio/midi',
4339 'mp2' => 'audio/mpeg',
4340 'mp3' => 'audio/mpeg',
4341 'mpga' => 'audio/mpeg',
4342 'aif' => 'audio/x-aiff',
4343 'aifc' => 'audio/x-aiff',
4344 'aiff' => 'audio/x-aiff',
4345 'ram' => 'audio/x-pn-realaudio',
4346 'rm' => 'audio/x-pn-realaudio',
4347 'rpm' => 'audio/x-pn-realaudio-plugin',
4348 'ra' => 'audio/x-realaudio',
4349 'wav' => 'audio/x-wav',
4350 'bmp' => 'image/bmp',
4351 'gif' => 'image/gif',
4352 'jpeg' => 'image/jpeg',
4353 'jpe' => 'image/jpeg',
4354 'jpg' => 'image/jpeg',
4355 'png' => 'image/png',
4356 'tiff' => 'image/tiff',
4357 'tif' => 'image/tiff',
4358 'eml' => 'message/rfc822',
4359 'css' => 'text/css',
4360 'html' => 'text/html',
4361 'htm' => 'text/html',
4362 'shtml' => 'text/html',
4363 'log' => 'text/plain',
4364 'text' => 'text/plain',
4365 'txt' => 'text/plain',
4366 'rtx' => 'text/richtext',
4367 'rtf' => 'text/rtf',
4368 'vcf' => 'text/vcard',
4369 'vcard' => 'text/vcard',
4370 'xml' => 'text/xml',
4371 'xsl' => 'text/xml',
4372 'mpeg' => 'video/mpeg',
4373 'mpe' => 'video/mpeg',
4374 'mpg' => 'video/mpeg',
4375 'mov' => 'video/quicktime',
4376 'qt' => 'video/quicktime',
4377 'rv' => 'video/vnd.rn-realvideo',
4378 'avi' => 'video/x-msvideo',
4379 'movie' => 'video/x-sgi-movie'
4380 );
4381 if (array_key_exists(strtolower($ext), $mimes)) {
4382 return $mimes[strtolower($ext)];
4383 }
4384 return 'application/octet-stream';
4385 }
4386
4387 /**
4388 * Map a file name to a MIME type.
4389 * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
4390 * @param string $filename A file name or full path, does not need to exist as a file
4391 * @return string
4392 * @static
4393 */
4394 public static function filenameToType($filename)
4395 {
4396 // In case the path is a URL, strip any query string before getting extension
4397 $qpos = strpos($filename, '?');
4398 if (false !== $qpos) {
4399 $filename = substr($filename, 0, $qpos);
4400 }
4401 $pathinfo = self::mb_pathinfo($filename);
4402 return self::_mime_types($pathinfo['extension']);
4403 }
4404
4405 /**
4406 * Multi-byte-safe pathinfo replacement.
4407 * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
4408 * Works similarly to the one in PHP >= 5.2.0
4409 * @link http://www.php.net/manual/en/function.pathinfo.php#107461
4410 * @param string $path A filename or path, does not need to exist as a file
4411 * @param integer|string $options Either a PATHINFO_* constant,
4412 * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
4413 * @return string|array
4414 * @static
4415 */
4416 public static function mb_pathinfo($path, $options = null)
4417 {
4418 $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
4419 $pathinfo = array();
4420 if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
4421 if (array_key_exists(1, $pathinfo)) {
4422 $ret['dirname'] = $pathinfo[1];
4423 }
4424 if (array_key_exists(2, $pathinfo)) {
4425 $ret['basename'] = $pathinfo[2];
4426 }
4427 if (array_key_exists(5, $pathinfo)) {
4428 $ret['extension'] = $pathinfo[5];
4429 }
4430 if (array_key_exists(3, $pathinfo)) {
4431 $ret['filename'] = $pathinfo[3];
4432 }
4433 }
4434 switch ($options) {
4435 case PATHINFO_DIRNAME:
4436 case 'dirname':
4437 return $ret['dirname'];
4438 case PATHINFO_BASENAME:
4439 case 'basename':
4440 return $ret['basename'];
4441 case PATHINFO_EXTENSION:
4442 case 'extension':
4443 return $ret['extension'];
4444 case PATHINFO_FILENAME:
4445 case 'filename':
4446 return $ret['filename'];
4447 default:
4448 return $ret;
4449 }
4450 }
4451
4452 /**
4453 * Set or reset instance properties.
4454 * You should avoid this function - it's more verbose, less efficient, more error-prone and
4455 * harder to debug than setting properties directly.
4456 * Usage Example:
4457 * `$mail->set('SMTPSecure', 'tls');`
4458 * is the same as:
4459 * `$mail->SMTPSecure = 'tls';`
4460 * @access public
4461 * @param string $name The property name to set
4462 * @param mixed $value The value to set the property to
4463 * @return boolean
4464 * @TODO Should this not be using the __set() magic function?
4465 */
4466 public function set($name, $value = '')
4467 {
4468 if (property_exists($this, $name)) {
4469 $this->$name = $value;
4470 return true;
4471 } else {
4472 $this->setError($this->lang('variable_set') . $name);
4473 return false;
4474 }
4475 }
4476
4477 /**
4478 * Strip newlines to prevent header injection.
4479 * @access public
4480 * @param string $str
4481 * @return string
4482 */
4483 public function secureHeader($str)
4484 {
4485 return trim(str_replace(array("\r", "\n"), '', $str));
4486 }
4487
4488 /**
4489 * Normalize line breaks in a string.
4490 * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
4491 * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
4492 * @param string $text
4493 * @param string $breaktype What kind of line break to use, defaults to CRLF
4494 * @return string
4495 * @access public
4496 * @static
4497 */
4498 public static function normalizeBreaks($text, $breaktype = "\r\n")
4499 {
4500 return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
4501 }
4502
4503
4504 /**
4505 * Set the public and private key files and password for S/MIME signing.
4506 * @access public
4507 * @param string $cert_filename
4508 * @param string $key_filename
4509 * @param string $key_pass Password for private key
4510 * @param string $extracerts_filename Optional path to chain certificate
4511 */
4512 public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
4513 {
4514 $this->sign_cert_file = $cert_filename;
4515 $this->sign_key_file = $key_filename;
4516 $this->sign_key_pass = $key_pass;
4517 $this->sign_extracerts_file = $extracerts_filename;
4518 }
4519
4520 /**
4521 * Quoted-Printable-encode a DKIM header.
4522 * @access public
4523 * @param string $txt
4524 * @return string
4525 */
4526 public function DKIM_QP($txt)
4527 {
4528 $line = '';
4529 for ($i = 0; $i < strlen($txt); $i++) {
4530 $ord = ord($txt[$i]);
4531 if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
4532 $line .= $txt[$i];
4533 } else {
4534 $line .= '=' . sprintf('%02X', $ord);
4535 }
4536 }
4537 return $line;
4538 }
4539
4540 /**
4541 * Generate a DKIM signature.
4542 * @access public
4543 * @param string $signHeader
4544 * @throws phpmailerException
4545 * @return string
4546 */
4547 public function DKIM_Sign($signHeader)
4548 {
4549 if (!defined('PKCS7_TEXT')) {
4550 if ($this->exceptions) {
4551 throw new phpmailerException($this->lang('extension_missing') . 'openssl');
4552 }
4553 return '';
4554 }
4555 $privKeyStr = file_get_contents($this->DKIM_private);
4556 if ($this->DKIM_passphrase != '') {
4557 $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
4558 } else {
4559 $privKey = $privKeyStr;
4560 }
4561 if (openssl_sign($signHeader, $signature, $privKey)) {
4562 return base64_encode($signature);
4563 }
4564 return '';
4565 }
4566
4567 /**
4568 * Generate a DKIM canonicalization header.
4569 * @access public
4570 * @param string $signHeader Header
4571 * @return string
4572 */
4573 public function DKIM_HeaderC($signHeader)
4574 {
4575 $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
4576 $lines = explode("\r\n", $signHeader);
4577 foreach ($lines as $key => $line) {
4578 list($heading, $value) = explode(':', $line, 2);
4579 $heading = strtolower($heading);
4580 $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
4581 $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
4582 }
4583 $signHeader = implode("\r\n", $lines);
4584 return $signHeader;
4585 }
4586
4587 /**
4588 * Generate a DKIM canonicalization body.
4589 * @access public
4590 * @param string $body Message Body
4591 * @return string
4592 */
4593 public function DKIM_BodyC($body)
4594 {
4595 if ($body == '') {
4596 return "\r\n";
4597 }
4598 // stabilize line endings
4599 $body = str_replace("\r\n", "\n", $body);
4600 $body = str_replace("\n", "\r\n", $body);
4601 // END stabilize line endings
4602 while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
4603 $body = substr($body, 0, strlen($body) - 2);
4604 }
4605 return $body;
4606 }
4607
4608 /**
4609 * Create the DKIM header and body in a new message header.
4610 * @access public
4611 * @param string $headers_line Header lines
4612 * @param string $subject Subject
4613 * @param string $body Body
4614 * @return string
4615 */
4616 public function DKIM_Add($headers_line, $subject, $body)
4617 {
4618 $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
4619 $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
4620 $DKIMquery = 'dns/txt'; // Query method
4621 $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
4622 $subject_header = "Subject: $subject";
4623 $headers = explode($this->LE, $headers_line);
4624 $from_header = '';
4625 $to_header = '';
4626 $current = '';
4627 foreach ($headers as $header) {
4628 if (strpos($header, 'From:') === 0) {
4629 $from_header = $header;
4630 $current = 'from_header';
4631 } elseif (strpos($header, 'To:') === 0) {
4632 $to_header = $header;
4633 $current = 'to_header';
4634 } else {
4635 if (!empty($$current) && strpos($header, ' =?') === 0) {
4636 $$current .= $header;
4637 } else {
4638 $current = '';
4639 }
4640 }
4641 }
4642 $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
4643 $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
4644 $subject = str_replace(
4645 '|',
4646 '=7C',
4647 $this->DKIM_QP($subject_header)
4648 ); // Copied header fields (dkim-quoted-printable)
4649 $body = $this->DKIM_BodyC($body);
4650 $DKIMlen = strlen($body); // Length of body
4651 $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
4652 if ('' == $this->DKIM_identity) {
4653 $ident = '';
4654 } else {
4655 $ident = ' i=' . $this->DKIM_identity . ';';
4656 }
4657 $dkimhdrs = 'DKIM-Signature: v=1; a=' .
4658 $DKIMsignatureType . '; q=' .
4659 $DKIMquery . '; l=' .
4660 $DKIMlen . '; s=' .
4661 $this->DKIM_selector .
4662 ";\r\n" .
4663 "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
4664 "\th=From:To:Subject;\r\n" .
4665 "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
4666 "\tz=$from\r\n" .
4667 "\t|$to\r\n" .
4668 "\t|$subject;\r\n" .
4669 "\tbh=" . $DKIMb64 . ";\r\n" .
4670 "\tb=";
4671 $toSign = $this->DKIM_HeaderC(
4672 $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs
4673 );
4674 $signed = $this->DKIM_Sign($toSign);
4675 return $dkimhdrs . $signed . "\r\n";
4676 }
4677
4678 /**
4679 * Detect if a string contains a line longer than the maximum line length allowed.
4680 * @param string $str
4681 * @return boolean
4682 * @static
4683 */
4684 public static function hasLineLongerThanMax($str)
4685 {
4686 //+2 to include CRLF line break for a 1000 total
4687 return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
4688 }
4689
4690 /**
4691 * Allows for public read access to 'to' property.
4692 * @access public
4693 * @return array
4694 */
4695 public function getToAddresses()
4696 {
4697 return $this->to;
4698 }
4699
4700 /**
4701 * Allows for public read access to 'cc' property.
4702 * @access public
4703 * @return array
4704 */
4705 public function getCcAddresses()
4706 {
4707 return $this->cc;
4708 }
4709
4710 /**
4711 * Allows for public read access to 'bcc' property.
4712 * @access public
4713 * @return array
4714 */
4715 public function getBccAddresses()
4716 {
4717 return $this->bcc;
4718 }
4719
4720 /**
4721 * Allows for public read access to 'ReplyTo' property.
4722 * @access public
4723 * @return array
4724 */
4725 public function getReplyToAddresses()
4726 {
4727 return $this->ReplyTo;
4728 }
4729
4730 /**
4731 * Allows for public read access to 'all_recipients' property.
4732 * @access public
4733 * @return array
4734 */
4735 public function getAllRecipientAddresses()
4736 {
4737 return $this->all_recipients;
4738 }
4739
4740 /**
4741 * Perform a callback.
4742 * @param boolean $isSent
4743 * @param array $to
4744 * @param array $cc
4745 * @param array $bcc
4746 * @param string $subject
4747 * @param string $body
4748 * @param string $from
4749 */
4750 protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
4751 {
4752 if (!empty($this->action_function) && is_callable($this->action_function)) {
4753 $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
4754 call_user_func_array($this->action_function, $params);
4755 }
4756 }
4757}
4758
4759/**
4760 * PHPMailer exception handler
4761 * @package PHPMailer
4762 */
4763class phpmailerException extends Exception
4764{
4765 /**
4766 * Prettify error message output
4767 * @return string
4768 */
4769 public function errorMessage()
4770 {
4771 $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
4772 return $errorMsg;
4773 }
4774}
4775
4776function lrtrim($string){
4777 return stripslashes(ltrim(rtrim($string)));
4778}
4779?>
4780
4781<!DOCTYPE html>
4782<html lang="en">
4783 <head>
4784 <meta charset="utf-8">
4785 <meta http-equiv="X-UA-Compatible" content="IE=edge">
4786 <meta name="viewport" content="width=device-width, initial-scale=1">
4787 <meta name="robots" content="noindex, nofollow">
4788 <title> IDBTE4M DUAL MAILER </title>
4789 <!-- Bootstrap -->
4790 <link rel="stylesheet" href="https://bootswatch.com/lumen/bootstrap.min.css">
4791<style>
4792body{
4793background-color: #444;
4794font: 10pt Verdana;
4795color: #fff;
4796}
4797tr,td,table,input,textarea {
4798BORDER-RIGHT: #3e3e3e 1px solid;
4799BORDER-TOP: #3e3e3e 1px solid;
4800BORDER-LEFT: #3e3e3e 1px solid;
4801BORDER-BOTTOM: #3e3e3e 1px solid;
4802}
4803#domain tr:hover{
4804background-color: #444;
4805}
4806td {
4807color: #2BA8EC;
4808}
4809.listdir td{
4810 text-align: center;
4811}
4812.listdir th{
4813 color: #FF9900;
4814}
4815.dir,.file
4816{
4817 text-align: left !important;
4818}
4819.dir{
4820 font-size: 10pt;
4821 font-weight: bold;
4822}
4823table {
4824BACKGROUND-COLOR: #111;
4825}
4826input {
4827BACKGROUND-COLOR: Black;
4828color: #ff9900;
4829}
4830input.submit {
4831text-shadow: 0pt 0pt 0.3em cyan, 0pt 0pt 0.3em cyan;
4832color: #FFFFFF;
4833border-color: #009900;
4834}
4835code {
4836border: dashed 0px #333;
4837color: while;
4838}
4839run {
4840border : dashed 0px #333;
4841color: #FF00AA;
4842}
4843textarea {
4844BACKGROUND-COLOR: #1b1b1b;
4845font: Fixedsys bold;
4846color: #aaa;
4847}
4848A:link {
4849 COLOR: #2BA8EC; TEXT-DECORATION: none
4850}
4851A:visited {
4852 COLOR: #2BA8EC; TEXT-DECORATION: none
4853}
4854A:hover {
4855 text-shadow: 0pt 0pt 0.3em cyan, 0pt 0pt 0.3em cyan;
4856 color: #FFFFFF; TEXT-DECORATION: none
4857}
4858A:active {
4859 color: Red; TEXT-DECORATION: none
4860}
4861.listdir tr:hover{
4862 background: #444;
4863}
4864.listdir tr:hover td{
4865 background: #444;
4866 text-shadow: 0pt 0pt 0.3em cyan, 0pt 0pt 0.3em cyan;
4867 color: #FFFFFF; TEXT-DECORATION: none;
4868}
4869.notline{
4870 background: #111;
4871}
4872.line{
4873 background: #222;
4874}
4875#mg img:hover {
4876-webkit-animation:tremer 0.5s linear infinite;
4877-moz-animation:tremer 0.5s linear infinite;
4878-o-animation:tremer 0.5s linear infinite;
4879animation:tremer 0.5s linear infinite;
4880}
4881#mg img{border:4px double yellow;
4882</style>
4883<font face='red' color='black'>
4884
4885 <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
4886 <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
4887 <!--[if lt IE 9]>
4888 <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
4889 <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
4890 <![endif]-->
4891 </head>
4892 <body>
4893
4894
4895<div class="table">
4896 <div class="mg">
4897 <div class="row">
4898 <div class="col-sm-12 col-lg-12">
4899
4900<div class="mg">
4901 <div class="well well-sm">
4902
4903<div class="mg" id="accordion" role="tablist" aria-multiselectable="true">
4904 <div class="mg">
4905 <div class="mg" role="tab" id="headingOne">
4906
4907 <form action="" method="post" id="form">
4908 <div class="row">
4909 <div class="textarea">
4910 <div class="form-group">
4911 <label for="name">
4912 Form Name</label>
4913 <input type="text" name="name" class="form-control" id="name" placeholder="Enter name" value="<?php if(isset($_POST['name'])){ echo $_POST['name']; }?>">
4914 </div>
4915 <div class="form-group">
4916 <label for="email">
4917 From Email</label>
4918 <div class="input-group">
4919 <span class="input-group-addon"><span class="glyphicon glyphicon-envelope"></span>
4920 </span>
4921 <input type="text" name="email" class="form-control" id="email" placeholder="Enter email" value="<?php if(isset($_POST['email'])){ echo $_POST['email']; }?>"></div>
4922 </div>
4923 <div class="form-group">
4924 <label for="email">
4925 Reply To</label>
4926 <div class="input-group">
4927 <span class="input-group-addon"><span class="glyphicon glyphicon-envelope"></span>
4928 </span>
4929 <input type="text" name="addReplyTo" class="form-control" id="addReplyTo" placeholder="Enter Reply To" value="<?php if(isset($_POST['addReplyTo'])){ echo $_POST['addReplyTo']; }?>"></div>
4930 </div>
4931 <div class="form-group">
4932 <label for="subject">
4933 Subject</label>
4934 <input type="text" name="subject" class="form-control" id="subject" placeholder="Enter subject" value="<?php if(isset($_POST['subject'])){ echo $_POST['subject']; }?>">
4935 </div>
4936 </div>
4937 <div class="textarea">
4938 <div class="form-group">
4939 <label for="name">
4940 Message</label>
4941 <textarea name="message" id="message" class="form-control" rows="12" cols="25" placeholder="Message"><?php if(isset($_POST['message'])){ echo $_POST['message']; }?></textarea>
4942 </div>
4943 </div>
4944
4945 <br />
4946
4947 <div class="textarea">
4948 <div class="form-group">
4949 <label for="mail">
4950 Mail Lists</label>
4951 <textarea name="mail" id="mail" class="form-control" rows="9" cols="25" required="required"
4952 placeholder="Mail Lists"><?php if(isset($_POST['mail'])){ echo $_POST['mail']; }?></textarea>
4953 </div>
4954 </div>
4955 <center>
4956 <div class="textarea">
4957 <input type="submit" class="btn btn-primary pull-right" id="btnContactUs" value="Send Letter">
4958 </div>
4959 </form>
4960 </center>
4961 <div class="link">
4962<?php
4963if(isset($_POST["mail"])){
4964
4965 echo '<br /><div class="alert alert alert-warning" role="alert"> Message is being sent .... </div>';
4966
4967 $list = $_POST["mail"];
4968 $emails = explode("\r\n",$list);
4969 $emailsCount = count($emails);
4970
4971 for($i = 0 ; $i < $emailsCount ; $i++) {
4972
4973 $to = $emails[$i];
4974 $to = str_replace(" ", "", $to);
4975
4976 $message = str_replace("&email&", $to, lrtrim($_POST['message']));
4977 $message = urlencode($message);
4978 $message = ereg_replace("%5C%22", "%22", $message);
4979 $message = urldecode($message);
4980 $message = stripslashes($message);
4981
4982 $subject = str_replace("&email&", $to, lrtrim($_POST['subject']));
4983 $subject = preg_replace('/([^a-z ])/ie', 'sprintf("=%02x",ord(StripSlashes("\\1")))', $subject);
4984 $subject = str_replace(' ', '_', $subject);
4985 $subject = "=?UTF-8?Q?$subject?=";
4986
4987 $qx=$i+1;
4988 print "<br/ >Line $qx . Sending mail to <b>$to</b> .... ";
4989 flush();
4990
4991 $mail = new PHPMailer();
4992 $mail->Priority = 1;
4993
4994 $mail->From = lrtrim($_POST['email']);
4995 $mail->FromName = lrtrim($_POST['name']);
4996
4997 $mail->addAddress(lrtrim($to));
4998 $mail->addReplyTo(lrtrim($_POST['addReplyTo']));
4999
5000 $mail->isHTML(true);
5001
5002 $mail->Subject = $subject;
5003 $mail->Body = str_replace("&time&" , date("F j, Y, g:i a"), $message);
5004
5005 if(!$mail->send()) {
5006 echo '<center><span style="color:red;">Message could not be sent.</center>';
5007 echo '<center>Mailer Error: <span class="label label-danger"> ' . $mail->ErrorInfo . ' </span></span></center>';
5008 } else {
5009 echo '<center>Message sent: <span class="label label-success">OK</span></center>';
5010 }
5011 }
5012 echo "<br /><br />";
5013}
5014?>
5015 </div>
5016 </div>
5017 </div>
5018
5019 </div>
5020 <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
5021 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
5022 <!-- Include all compiled plugins (below), or include individual files as needed -->
5023 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
5024 <script>
5025/*! jQuery Validation Plugin - v1.13.1 - 10/14/2014
5026 * http://jqueryvalidation.org/
5027 * Copyright (c) 2014 Jörn Zaefferer; Licensed MIT */
5028!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){a.extend(a.fn,{validate:function(b){if(!this.length)return void(b&&b.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."));var c=a.data(this[0],"validator");return c?c:(this.attr("novalidate","novalidate"),c=new a.validator(b,this[0]),a.data(this[0],"validator",c),c.settings.onsubmit&&(this.validateDelegate(":submit","click",function(b){c.settings.submitHandler&&(c.submitButton=b.target),a(b.target).hasClass("cancel")&&(c.cancelSubmit=!0),void 0!==a(b.target).attr("formnovalidate")&&(c.cancelSubmit=!0)}),this.submit(function(b){function d(){var d,e;return c.settings.submitHandler?(c.submitButton&&(d=a("<input type='hidden'/>").attr("name",c.submitButton.name).val(a(c.submitButton).val()).appendTo(c.currentForm)),e=c.settings.submitHandler.call(c,c.currentForm,b),c.submitButton&&d.remove(),void 0!==e?e:!1):!0}return c.settings.debug&&b.preventDefault(),c.cancelSubmit?(c.cancelSubmit=!1,d()):c.form()?c.pendingRequest?(c.formSubmitted=!0,!1):d():(c.focusInvalid(),!1)})),c)},valid:function(){var b,c;return a(this[0]).is("form")?b=this.validate().form():(b=!0,c=a(this[0].form).validate(),this.each(function(){b=c.element(this)&&b})),b},removeAttrs:function(b){var c={},d=this;return a.each(b.split(/\s/),function(a,b){c[b]=d.attr(b),d.removeAttr(b)}),c},rules:function(b,c){var d,e,f,g,h,i,j=this[0];if(b)switch(d=a.data(j.form,"validator").settings,e=d.rules,f=a.validator.staticRules(j),b){case"add":a.extend(f,a.validator.normalizeRule(c)),delete f.messages,e[j.name]=f,c.messages&&(d.messages[j.name]=a.extend(d.messages[j.name],c.messages));break;case"remove":return c?(i={},a.each(c.split(/\s/),function(b,c){i[c]=f[c],delete f[c],"required"===c&&a(j).removeAttr("aria-required")}),i):(delete e[j.name],f)}return g=a.validator.normalizeRules(a.extend({},a.validator.classRules(j),a.validator.attributeRules(j),a.validator.dataRules(j),a.validator.staticRules(j)),j),g.required&&(h=g.required,delete g.required,g=a.extend({required:h},g),a(j).attr("aria-required","true")),g.remote&&(h=g.remote,delete g.remote,g=a.extend(g,{remote:h})),g}}),a.extend(a.expr[":"],{blank:function(b){return!a.trim(""+a(b).val())},filled:function(b){return!!a.trim(""+a(b).val())},unchecked:function(b){return!a(b).prop("checked")}}),a.validator=function(b,c){this.settings=a.extend(!0,{},a.validator.defaults,b),this.currentForm=c,this.init()},a.validator.format=function(b,c){return 1===arguments.length?function(){var c=a.makeArray(arguments);return c.unshift(b),a.validator.format.apply(this,c)}:(arguments.length>2&&c.constructor!==Array&&(c=a.makeArray(arguments).slice(1)),c.constructor!==Array&&(c=[c]),a.each(c,function(a,c){b=b.replace(new RegExp("\\{"+a+"\\}","g"),function(){return c})}),b)},a.extend(a.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:a([]),errorLabelContainer:a([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(a){this.lastActive=a,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(a)))},onfocusout:function(a){this.checkable(a)||!(a.name in this.submitted)&&this.optional(a)||this.element(a)},onkeyup:function(a,b){(9!==b.which||""!==this.elementValue(a))&&(a.name in this.submitted||a===this.lastElement)&&this.element(a)},onclick:function(a){a.name in this.submitted?this.element(a):a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).addClass(c).removeClass(d):a(b).addClass(c).removeClass(d)},unhighlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).removeClass(c).addClass(d):a(b).removeClass(c).addClass(d)}},setDefaults:function(b){a.extend(a.validator.defaults,b)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date ( ISO ).",number:"Please enter a valid number.",digits:"Please enter only digits.",creditcard:"Please enter a valid credit card number.",equalTo:"Please enter the same value again.",maxlength:a.validator.format("Please enter no more than {0} characters."),minlength:a.validator.format("Please enter at least {0} characters."),rangelength:a.validator.format("Please enter a value between {0} and {1} characters long."),range:a.validator.format("Please enter a value between {0} and {1}."),max:a.validator.format("Please enter a value less than or equal to {0}."),min:a.validator.format("Please enter a value greater than or equal to {0}.")},autoCreateRanges:!1,prototype:{init:function(){function b(b){var c=a.data(this[0].form,"validator"),d="on"+b.type.replace(/^validate/,""),e=c.settings;e[d]&&!this.is(e.ignore)&&e[d].call(c,this[0],b)}this.labelContainer=a(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||a(this.currentForm),this.containers=a(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var c,d=this.groups={};a.each(this.settings.groups,function(b,c){"string"==typeof c&&(c=c.split(/\s/)),a.each(c,function(a,c){d[c]=b})}),c=this.settings.rules,a.each(c,function(b,d){c[b]=a.validator.normalizeRule(d)}),a(this.currentForm).validateDelegate(":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'] ,[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox']","focusin focusout keyup",b).validateDelegate("select, option, [type='radio'], [type='checkbox']","click",b),this.settings.invalidHandler&&a(this.currentForm).bind("invalid-form.validate",this.settings.invalidHandler),a(this.currentForm).find("[required], [data-rule-required], .required").attr("aria-required","true")},form:function(){return this.checkForm(),a.extend(this.submitted,this.errorMap),this.invalid=a.extend({},this.errorMap),this.valid()||a(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(b){var c=this.clean(b),d=this.validationTargetFor(c),e=!0;return this.lastElement=d,void 0===d?delete this.invalid[c.name]:(this.prepareElement(d),this.currentElements=a(d),e=this.check(d)!==!1,e?delete this.invalid[d.name]:this.invalid[d.name]=!0),a(b).attr("aria-invalid",!e),this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),e},showErrors:function(b){if(b){a.extend(this.errorMap,b),this.errorList=[];for(var c in b)this.errorList.push({message:b[c],element:this.findByName(c)[0]});this.successList=a.grep(this.successList,function(a){return!(a.name in b)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){a.fn.resetForm&&a(this.currentForm).resetForm(),this.submitted={},this.lastElement=null,this.prepareForm(),this.hideErrors(),this.elements().removeClass(this.settings.errorClass).removeData("previousValue").removeAttr("aria-invalid")},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b,c=0;for(b in a)c++;return c},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(a){a.not(this.containers).text(""),this.addWrapper(a).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{a(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(b){}},findLastActive:function(){var b=this.lastActive;return b&&1===a.grep(this.errorList,function(a){return a.element.name===b.name}).length&&b},elements:function(){var b=this,c={};return a(this.currentForm).find("input, select, textarea").not(":submit, :reset, :image, [disabled], [readonly]").not(this.settings.ignore).filter(function(){return!this.name&&b.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.name in c||!b.objectLength(a(this).rules())?!1:(c[this.name]=!0,!0)})},clean:function(b){return a(b)[0]},errors:function(){var b=this.settings.errorClass.split(" ").join(".");return a(this.settings.errorElement+"."+b,this.errorContext)},reset:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=a([]),this.toHide=a([]),this.currentElements=a([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset(),this.toHide=this.errorsFor(a)},elementValue:function(b){var c,d=a(b),e=b.type;return"radio"===e||"checkbox"===e?a("input[name='"+b.name+"']:checked").val():"number"===e&&"undefined"!=typeof b.validity?b.validity.badInput?!1:d.val():(c=d.val(),"string"==typeof c?c.replace(/\r/g,""):c)},check:function(b){b=this.validationTargetFor(this.clean(b));var c,d,e,f=a(b).rules(),g=a.map(f,function(a,b){return b}).length,h=!1,i=this.elementValue(b);for(d in f){e={method:d,parameters:f[d]};try{if(c=a.validator.methods[d].call(this,i,b,e.parameters),"dependency-mismatch"===c&&1===g){h=!0;continue}if(h=!1,"pending"===c)return void(this.toHide=this.toHide.not(this.errorsFor(b)));if(!c)return this.formatAndAdd(b,e),!1}catch(j){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+b.id+", check the '"+e.method+"' method.",j),j}}if(!h)return this.objectLength(f)&&this.successList.push(b),!0},customDataMessage:function(b,c){return a(b).data("msg"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase())||a(b).data("msg")},customMessage:function(a,b){var c=this.settings.messages[a];return c&&(c.constructor===String?c:c[b])},findDefined:function(){for(var a=0;a<arguments.length;a++)if(void 0!==arguments[a])return arguments[a];return void 0},defaultMessage:function(b,c){return this.findDefined(this.customMessage(b.name,c),this.customDataMessage(b,c),!this.settings.ignoreTitle&&b.title||void 0,a.validator.messages[c],"<strong>Warning: No message defined for "+b.name+"</strong>")},formatAndAdd:function(b,c){var d=this.defaultMessage(b,c.method),e=/\$?\{(\d+)\}/g;"function"==typeof d?d=d.call(this,c.parameters,b):e.test(d)&&(d=a.validator.format(d.replace(e,"{$1}"),c.parameters)),this.errorList.push({message:d,element:b,method:c.method}),this.errorMap[b.name]=d,this.submitted[b.name]=d},addWrapper:function(a){return this.settings.wrapper&&(a=a.add(a.parent(this.settings.wrapper))),a},defaultShowErrors:function(){var a,b,c;for(a=0;this.errorList[a];a++)c=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,c.element,this.settings.errorClass,this.settings.validClass),this.showLabel(c.element,c.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return a(this.errorList).map(function(){return this.element})},showLabel:function(b,c){var d,e,f,g=this.errorsFor(b),h=this.idOrName(b),i=a(b).attr("aria-describedby");g.length?(g.removeClass(this.settings.validClass).addClass(this.settings.errorClass),g.html(c)):(g=a("<"+this.settings.errorElement+">").attr("id",h+"-error").addClass(this.settings.errorClass).html(c||""),d=g,this.settings.wrapper&&(d=g.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(d):this.settings.errorPlacement?this.settings.errorPlacement(d,a(b)):d.insertAfter(b),g.is("label")?g.attr("for",h):0===g.parents("label[for='"+h+"']").length&&(f=g.attr("id").replace(/(:|\.|\[|\])/g,"\\$1"),i?i.match(new RegExp("\\b"+f+"\\b"))||(i+=" "+f):i=f,a(b).attr("aria-describedby",i),e=this.groups[b.name],e&&a.each(this.groups,function(b,c){c===e&&a("[name='"+b+"']",this.currentForm).attr("aria-describedby",g.attr("id"))}))),!c&&this.settings.success&&(g.text(""),"string"==typeof this.settings.success?g.addClass(this.settings.success):this.settings.success(g,b)),this.toShow=this.toShow.add(g)},errorsFor:function(b){var c=this.idOrName(b),d=a(b).attr("aria-describedby"),e="label[for='"+c+"'], label[for='"+c+"'] *";return d&&(e=e+", #"+d.replace(/\s+/g,", #")),this.errors().filter(e)},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},validationTargetFor:function(b){return this.checkable(b)&&(b=this.findByName(b.name)),a(b).not(this.settings.ignore)[0]},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(b){return a(this.currentForm).find("[name='"+b+"']")},getLength:function(b,c){switch(c.nodeName.toLowerCase()){case"select":return a("option:selected",c).length;case"input":if(this.checkable(c))return this.findByName(c.name).filter(":checked").length}return b.length},depend:function(a,b){return this.dependTypes[typeof a]?this.dependTypes[typeof a](a,b):!0},dependTypes:{"boolean":function(a){return a},string:function(b,c){return!!a(b,c.form).length},"function":function(a,b){return a(b)}},optional:function(b){var c=this.elementValue(b);return!a.validator.methods.required.call(this,c,b)&&"dependency-mismatch"},startRequest:function(a){this.pending[a.name]||(this.pendingRequest++,this.pending[a.name]=!0)},stopRequest:function(b,c){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[b.name],c&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(a(this.currentForm).submit(),this.formSubmitted=!1):!c&&0===this.pendingRequest&&this.formSubmitted&&(a(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(b){return a.data(b,"previousValue")||a.data(b,"previousValue",{old:null,valid:!0,message:this.defaultMessage(b,"remote")})}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(b,c){b.constructor===String?this.classRuleSettings[b]=c:a.extend(this.classRuleSettings,b)},classRules:function(b){var c={},d=a(b).attr("class");return d&&a.each(d.split(" "),function(){this in a.validator.classRuleSettings&&a.extend(c,a.validator.classRuleSettings[this])}),c},attributeRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)"required"===c?(d=b.getAttribute(c),""===d&&(d=!0),d=!!d):d=f.attr(c),/min|max/.test(c)&&(null===g||/number|range|text/.test(g))&&(d=Number(d)),d||0===d?e[c]=d:g===c&&"range"!==g&&(e[c]=!0);return e.maxlength&&/-1|2147483647|524288/.test(e.maxlength)&&delete e.maxlength,e},dataRules:function(b){var c,d,e={},f=a(b);for(c in a.validator.methods)d=f.data("rule"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase()),void 0!==d&&(e[c]=d);return e},staticRules:function(b){var c={},d=a.data(b.form,"validator");return d.settings.rules&&(c=a.validator.normalizeRule(d.settings.rules[b.name])||{}),c},normalizeRules:function(b,c){return a.each(b,function(d,e){if(e===!1)return void delete b[d];if(e.param||e.depends){var f=!0;switch(typeof e.depends){case"string":f=!!a(e.depends,c.form).length;break;case"function":f=e.depends.call(c,c)}f?b[d]=void 0!==e.param?e.param:!0:delete b[d]}}),a.each(b,function(d,e){b[d]=a.isFunction(e)?e(c):e}),a.each(["minlength","maxlength"],function(){b[this]&&(b[this]=Number(b[this]))}),a.each(["rangelength","range"],function(){var c;b[this]&&(a.isArray(b[this])?b[this]=[Number(b[this][0]),Number(b[this][1])]:"string"==typeof b[this]&&(c=b[this].replace(/[\[\]]/g,"").split(/[\s,]+/),b[this]=[Number(c[0]),Number(c[1])]))}),a.validator.autoCreateRanges&&(null!=b.min&&null!=b.max&&(b.range=[b.min,b.max],delete b.min,delete b.max),null!=b.minlength&&null!=b.maxlength&&(b.rangelength=[b.minlength,b.maxlength],delete b.minlength,delete b.maxlength)),b},normalizeRule:function(b){if("string"==typeof b){var c={};a.each(b.split(/\s/),function(){c[this]=!0}),b=c}return b},addMethod:function(b,c,d){a.validator.methods[b]=c,a.validator.messages[b]=void 0!==d?d:a.validator.messages[b],c.length<3&&a.validator.addClassRules(b,a.validator.normalizeRule(b))},methods:{required:function(b,c,d){if(!this.depend(d,c))return"dependency-mismatch";if("select"===c.nodeName.toLowerCase()){var e=a(c).val();return e&&e.length>0}return this.checkable(c)?this.getLength(b,c)>0:a.trim(b).length>0},email:function(a,b){return this.optional(b)||/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(a)},url:function(a,b){return this.optional(b)||/^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(a)},date:function(a,b){return this.optional(b)||!/Invalid|NaN/.test(new Date(a).toString())},dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(a)},number:function(a,b){return this.optional(b)||/^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},creditcard:function(a,b){if(this.optional(b))return"dependency-mismatch";if(/[^0-9 \-]+/.test(a))return!1;var c,d,e=0,f=0,g=!1;if(a=a.replace(/\D/g,""),a.length<13||a.length>19)return!1;for(c=a.length-1;c>=0;c--)d=a.charAt(c),f=parseInt(d,10),g&&(f*=2)>9&&(f-=9),e+=f,g=!g;return e%10===0},minlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d},maxlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||d>=e},rangelength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d[0]&&e<=d[1]},min:function(a,b,c){return this.optional(b)||a>=c},max:function(a,b,c){return this.optional(b)||c>=a},range:function(a,b,c){return this.optional(b)||a>=c[0]&&a<=c[1]},equalTo:function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.unbind(".validate-equalTo").bind("blur.validate-equalTo",function(){a(c).valid()}),b===e.val()},remote:function(b,c,d){if(this.optional(c))return"dependency-mismatch";var e,f,g=this.previousValue(c);return this.settings.messages[c.name]||(this.settings.messages[c.name]={}),g.originalMessage=this.settings.messages[c.name].remote,this.settings.messages[c.name].remote=g.message,d="string"==typeof d&&{url:d}||d,g.old===b?g.valid:(g.old=b,e=this,this.startRequest(c),f={},f[c.name]=b,a.ajax(a.extend(!0,{url:d,mode:"abort",port:"validate"+c.name,dataType:"json",data:f,context:e.currentForm,success:function(d){var f,h,i,j=d===!0||"true"===d;e.settings.messages[c.name].remote=g.originalMessage,j?(i=e.formSubmitted,e.prepareElement(c),e.formSubmitted=i,e.successList.push(c),delete e.invalid[c.name],e.showErrors()):(f={},h=d||e.defaultMessage(c,"remote"),f[c.name]=g.message=a.isFunction(h)?h(b):h,e.invalid[c.name]=!0,e.showErrors(f)),g.valid=j,e.stopRequest(c,j)}},d)),"pending")}}}),a.format=function(){throw"$.format has been deprecated. Please use $.validator.format instead."};var b,c={};a.ajaxPrefilter?a.ajaxPrefilter(function(a,b,d){var e=a.port;"abort"===a.mode&&(c[e]&&c[e].abort(),c[e]=d)}):(b=a.ajax,a.ajax=function(d){var e=("mode"in d?d:a.ajaxSettings).mode,f=("port"in d?d:a.ajaxSettings).port;return"abort"===e?(c[f]&&c[f].abort(),c[f]=b.apply(this,arguments),c[f]):b.apply(this,arguments)}),a.extend(a.fn,{validateDelegate:function(b,c,d){return this.bind(c,function(c){var e=a(c.target);return e.is(b)?d.apply(e,arguments):void 0})}})});
5029
5030
5031$(document).ready(function(){
5032 $("#form").validate({
5033 rules: {
5034 name: { required: true , minlength: 2 },
5035 email: { required: true , minlength: 2 },
5036 subject: { required: true , minlength: 2 },
5037 message: { required: true , minlength: 2 },
5038 mail: { required: true , minlength: 2 },
5039 },
5040 messages: {
5041 name: "",
5042 email: "",
5043 subject: "",
5044 message: "",
5045 mail: "",
5046 },
5047 submitHandler: function(form) {
5048 form.submit();
5049 }
5050 });
5051});
5052 </script>
5053 <style>
5054 label.error {
5055 display: none!important;
5056 visibility: hidden!important;
5057 }
5058 </style>
5059 </body>
5060</html>