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