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