Newer
Older
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
}
/**
* Add a string or binary attachment (non-filesystem).
* This method can be used to attach ascii or binary data,
* such as a BLOB record from a database.
* @param string $string String attachment data.
* @param string $filename Name of the attachment.
* @param string $encoding File encoding (see $Encoding).
* @param string $type File extension (MIME) type.
* @param string $disposition Disposition to use
* @return void
*/
public function addStringAttachment(
$string,
$filename,
$encoding = 'base64',
$type = '',
$disposition = 'attachment'
) {
// If a MIME type is not specified, try to work it out from the file name
if ($type == '') {
$type = self::filenameToType($filename);
}
// Append to $attachment array
$this->attachment[] = array(
0 => $string,
1 => $filename,
2 => basename($filename),
3 => $encoding,
4 => $type,
5 => true, // isStringAttachment
6 => $disposition,
7 => 0
);
}
/**
* Add an embedded (inline) attachment from a file.
* This can include images, sounds, and just about any other document type.
* These differ from 'regular' attachments in that they are intended to be
* displayed inline with the message, not just attached for download.
* This is used in HTML messages that embed the images
* the HTML refers to using the $cid value.
* Never use a user-supplied path to a file!
* @param string $path Path to the attachment.
* @param string $cid Content ID of the attachment; Use this to reference
* the content when using an embedded image in HTML.
* @param string $name Overrides the attachment name.
* @param string $encoding File encoding (see $Encoding).
* @param string $type File MIME type.
* @param string $disposition Disposition to use
* @return boolean True on successfully adding an attachment
*/
public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
{
if (!self::isPermittedPath($path) or !@is_file($path)) {
$this->setError($this->lang('file_access') . $path);
return false;
}
// If a MIME type is not specified, try to work it out from the file name
if ($type == '') {
$type = self::filenameToType($path);
}
$filename = basename($path);
if ($name == '') {
$name = $filename;
}
// Append to $attachment array
$this->attachment[] = array(
0 => $path,
1 => $filename,
2 => $name,
3 => $encoding,
4 => $type,
5 => false, // isStringAttachment
6 => $disposition,
7 => $cid
);
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
/**
* Add an embedded stringified attachment.
* This can include images, sounds, and just about any other document type.
* Be sure to set the $type to an image type for images:
* JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
* @param string $string The attachment binary data.
* @param string $cid Content ID of the attachment; Use this to reference
* the content when using an embedded image in HTML.
* @param string $name
* @param string $encoding File encoding (see $Encoding).
* @param string $type MIME type.
* @param string $disposition Disposition to use
* @return boolean True on successfully adding an attachment
*/
public function addStringEmbeddedImage(
$string,
$cid,
$name = '',
$encoding = 'base64',
$type = '',
$disposition = 'inline'
) {
// If a MIME type is not specified, try to work it out from the name
if ($type == '' and !empty($name)) {
$type = self::filenameToType($name);
}
// Append to $attachment array
$this->attachment[] = array(
0 => $string,
1 => $name,
2 => $name,
3 => $encoding,
4 => $type,
5 => true, // isStringAttachment
6 => $disposition,
7 => $cid
);
return true;
}
/**
* Check if an inline attachment is present.
* @access public
* @return boolean
*/
public function inlineImageExists()
{
foreach ($this->attachment as $attachment) {
if ($attachment[6] == 'inline') {
return true;
}
return false;
}
/**
* Check if an attachment (non-inline) is present.
* @return boolean
*/
public function attachmentExists()
{
foreach ($this->attachment as $attachment) {
if ($attachment[6] == 'attachment') {
return true;
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
}
return false;
}
/**
* Check if this message has an alternative body set.
* @return boolean
*/
public function alternativeExists()
{
return !empty($this->AltBody);
}
/**
* Clear queued addresses of given kind.
* @access protected
* @param string $kind 'to', 'cc', or 'bcc'
* @return void
*/
public function clearQueuedAddresses($kind)
{
$RecipientsQueue = $this->RecipientsQueue;
foreach ($RecipientsQueue as $address => $params) {
if ($params[0] == $kind) {
unset($this->RecipientsQueue[$address]);
}
}
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
/**
* Clear all To recipients.
* @return void
*/
public function clearAddresses()
{
foreach ($this->to as $to) {
unset($this->all_recipients[strtolower($to[0])]);
}
$this->to = array();
$this->clearQueuedAddresses('to');
}
/**
* Clear all CC recipients.
* @return void
*/
public function clearCCs()
{
foreach ($this->cc as $cc) {
unset($this->all_recipients[strtolower($cc[0])]);
}
$this->cc = array();
$this->clearQueuedAddresses('cc');
}
/**
* Clear all BCC recipients.
* @return void
*/
public function clearBCCs()
{
foreach ($this->bcc as $bcc) {
unset($this->all_recipients[strtolower($bcc[0])]);
}
$this->bcc = array();
$this->clearQueuedAddresses('bcc');
}
/**
* Clear all ReplyTo recipients.
* @return void
*/
public function clearReplyTos()
{
$this->ReplyTo = array();
$this->ReplyToQueue = array();
}
/**
* Clear all recipient types.
* @return void
*/
public function clearAllRecipients()
{
$this->to = array();
$this->cc = array();
$this->bcc = array();
$this->all_recipients = array();
$this->RecipientsQueue = array();
}
/**
* Clear all filesystem, string, and binary attachments.
* @return void
*/
public function clearAttachments()
{
$this->attachment = array();
}
/**
* Clear all custom headers.
* @return void
*/
public function clearCustomHeaders()
{
$this->CustomHeader = array();
}
/**
* Add an error message to the error container.
* @access protected
* @param string $msg
* @return void
*/
protected function setError($msg)
{
$this->error_count++;
if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
$lasterror = $this->smtp->getError();
if (!empty($lasterror['error'])) {
$msg .= $this->lang('smtp_error') . $lasterror['error'];
if (!empty($lasterror['detail'])) {
$msg .= ' Detail: '. $lasterror['detail'];
}
if (!empty($lasterror['smtp_code'])) {
$msg .= ' SMTP code: ' . $lasterror['smtp_code'];
}
if (!empty($lasterror['smtp_code_ex'])) {
$msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
}
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
$this->ErrorInfo = $msg;
}
/**
* Return an RFC 822 formatted date.
* @access public
* @return string
* @static
*/
public static function rfcDate()
{
// Set the time zone to whatever the default is to avoid 500 errors
// Will default to UTC if it's not set properly in php.ini
date_default_timezone_set(@date_default_timezone_get());
return date('D, j M Y H:i:s O');
}
/**
* Get the server hostname.
* Returns 'localhost.localdomain' if unknown.
* @access protected
* @return string
*/
protected function serverHostname()
{
$result = 'localhost.localdomain';
if (!empty($this->Hostname)) {
$result = $this->Hostname;
} elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
$result = $_SERVER['SERVER_NAME'];
} elseif (function_exists('gethostname') && gethostname() !== false) {
$result = gethostname();
} elseif (php_uname('n') !== false) {
$result = php_uname('n');
}
return $result;
}
/**
* Get an error message in the current language.
* @access protected
* @param string $key
* @return string
*/
protected function lang($key)
{
if (count($this->language) < 1) {
$this->setLanguage('en'); // set the default language
}
if (array_key_exists($key, $this->language)) {
if ($key == 'smtp_connect_failed') {
//Include a link to troubleshooting docs on SMTP connection failure
//this is by far the biggest cause of support questions
//but it's usually not PHPMailer's fault.
return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
}
return $this->language[$key];
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
//Return the key as a fallback
return $key;
}
}
/**
* Check if an error occurred.
* @access public
* @return boolean True if an error did occur.
*/
public function isError()
{
return ($this->error_count > 0);
}
/**
* Ensure consistent line endings in a string.
* Changes every end of line from CRLF, CR or LF to $this->LE.
* @access public
* @param string $str String to fixEOL
* @return string
*/
public function fixEOL($str)
{
// Normalise to \n
$nstr = str_replace(array("\r\n", "\r"), "\n", $str);
// Now convert LE as needed
if ($this->LE !== "\n") {
$nstr = str_replace("\n", $this->LE, $nstr);
}
return $nstr;
}
/**
* Add a custom header.
* $name value can be overloaded to contain
* both header name and value (name:value)
* @access public
* @param string $name Custom header name
* @param string $value Header value
* @return void
*/
public function addCustomHeader($name, $value = null)
{
if ($value === null) {
// Value passed in as name:value
$this->CustomHeader[] = explode(':', $name, 2);
$this->CustomHeader[] = array($name, $value);
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
/**
* Returns all custom headers.
* @return array
*/
public function getCustomHeaders()
{
return $this->CustomHeader;
}
/**
* Create a message body from an HTML string.
* Automatically inlines images and creates a plain-text version by converting the HTML,
* overwriting any existing values in Body and AltBody.
* Do not source $message content from user input!
* $basedir is prepended when handling relative URLs, e.g. <img src="/images/a.png"> and must not be empty
* will look for an image file in $basedir/images/a.png and convert it to inline.
* If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email)
* If you don't want to apply these transformations to your HTML, just set Body and AltBody directly.
* @access public
* @param string $message HTML message string
* @param string $basedir Absolute path to a base directory to prepend to relative paths to images
* @param boolean|callable $advanced Whether to use the internal HTML to text converter
* or your own custom converter @see PHPMailer::html2text()
* @return string $message The transformed message Body
*/
public function msgHTML($message, $basedir = '', $advanced = false)
{
preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
if (array_key_exists(2, $images)) {
if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
// Ensure $basedir has a trailing /
$basedir .= '/';
}
foreach ($images[2] as $imgindex => $url) {
// Convert data URIs into embedded images
if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
$data = substr($url, strpos($url, ','));
if ($match[2]) {
$data = base64_decode($data);
} else {
$data = rawurldecode($data);
}
$cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
$message = str_replace(
$images[0][$imgindex],
$images[1][$imgindex] . '="cid:' . $cid . '"',
$message
);
}
continue;
}
if (
// Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
!empty($basedir)
// Ignore URLs containing parent dir traversal (..)
&& (strpos($url, '..') === false)
// Do not change urls that are already inline images
&& substr($url, 0, 4) !== 'cid:'
// Do not change absolute URLs, including anonymous protocol
&& !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url)
) {
$filename = basename($url);
$directory = dirname($url);
if ($directory == '.') {
$directory = '';
}
$cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
if (strlen($directory) > 1 && substr($directory, -1) != '/') {
$directory .= '/';
}
if ($this->addEmbeddedImage(
$basedir . $directory . $filename,
$cid,
$filename,
'base64',
self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
)
) {
$message = preg_replace(
'/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
$images[1][$imgindex] . '="cid:' . $cid . '"',
$message
);
}
}
}
}
$this->isHTML(true);
// Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
$this->Body = $this->normalizeBreaks($message);
$this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
if (!$this->alternativeExists()) {
$this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
self::CRLF . self::CRLF;
}
return $this->Body;
}
/**
* Convert an HTML string into plain text.
* This is used by msgHTML().
* Note - older versions of this function used a bundled advanced converter
* which was been removed for license reasons in #232.
* Example usage:
* <code>
* // Use default conversion
* $plain = $mail->html2text($html);
* // Use your own custom converter
* $plain = $mail->html2text($html, function($html) {
* $converter = new MyHtml2text($html);
* return $converter->get_text();
* });
* </code>
* @param string $html The HTML text to convert
* @param boolean|callable $advanced Any boolean value to use the internal converter,
* or provide your own callable for custom conversion.
* @return string
*/
public function html2text($html, $advanced = false)
{
if (is_callable($advanced)) {
return call_user_func($advanced, $html);
}
return html_entity_decode(
trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
ENT_QUOTES,
$this->CharSet
);
}
/**
* Get the MIME type for a file extension.
* @param string $ext File extension
* @access public
* @return string MIME type of file.
* @static
*/
public static function _mime_types($ext = '')
{
$mimes = array(
'xl' => 'application/excel',
'js' => 'application/javascript',
'hqx' => 'application/mac-binhex40',
'cpt' => 'application/mac-compactpro',
'bin' => 'application/macbinary',
'doc' => 'application/msword',
'word' => 'application/msword',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'class' => 'application/octet-stream',
'dll' => 'application/octet-stream',
'dms' => 'application/octet-stream',
'exe' => 'application/octet-stream',
'lha' => 'application/octet-stream',
'lzh' => 'application/octet-stream',
'psd' => 'application/octet-stream',
'sea' => 'application/octet-stream',
'so' => 'application/octet-stream',
'oda' => 'application/oda',
'pdf' => 'application/pdf',
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'ps' => 'application/postscript',
'smi' => 'application/smil',
'smil' => 'application/smil',
'mif' => 'application/vnd.mif',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'wbxml' => 'application/vnd.wap.wbxml',
'wmlc' => 'application/vnd.wap.wmlc',
'dcr' => 'application/x-director',
'dir' => 'application/x-director',
'dxr' => 'application/x-director',
'dvi' => 'application/x-dvi',
'gtar' => 'application/x-gtar',
'php3' => 'application/x-httpd-php',
'php4' => 'application/x-httpd-php',
'php' => 'application/x-httpd-php',
'phtml' => 'application/x-httpd-php',
'phps' => 'application/x-httpd-php-source',
'swf' => 'application/x-shockwave-flash',
'sit' => 'application/x-stuffit',
'tar' => 'application/x-tar',
'tgz' => 'application/x-tar',
'xht' => 'application/xhtml+xml',
'xhtml' => 'application/xhtml+xml',
'zip' => 'application/zip',
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mp2' => 'audio/mpeg',
'mp3' => 'audio/mpeg',
'mpga' => 'audio/mpeg',
'aif' => 'audio/x-aiff',
'aifc' => 'audio/x-aiff',
'aiff' => 'audio/x-aiff',
'ram' => 'audio/x-pn-realaudio',
'rm' => 'audio/x-pn-realaudio',
'rpm' => 'audio/x-pn-realaudio-plugin',
'ra' => 'audio/x-realaudio',
'wav' => 'audio/x-wav',
'bmp' => 'image/bmp',
'gif' => 'image/gif',
'jpeg' => 'image/jpeg',
'jpe' => 'image/jpeg',
'jpg' => 'image/jpeg',
'png' => 'image/png',
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'eml' => 'message/rfc822',
'css' => 'text/css',
'html' => 'text/html',
'htm' => 'text/html',
'shtml' => 'text/html',
'log' => 'text/plain',
'text' => 'text/plain',
'txt' => 'text/plain',
'rtx' => 'text/richtext',
'rtf' => 'text/rtf',
'vcf' => 'text/vcard',
'vcard' => 'text/vcard',
'xml' => 'text/xml',
'xsl' => 'text/xml',
'mpeg' => 'video/mpeg',
'mpe' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mov' => 'video/quicktime',
'qt' => 'video/quicktime',
'rv' => 'video/vnd.rn-realvideo',
'avi' => 'video/x-msvideo',
'movie' => 'video/x-sgi-movie'
);
if (array_key_exists(strtolower($ext), $mimes)) {
return $mimes[strtolower($ext)];
}
return 'application/octet-stream';
}
/**
* Map a file name to a MIME type.
* Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
* @param string $filename A file name or full path, does not need to exist as a file
* @return string
* @static
*/
public static function filenameToType($filename)
{
// In case the path is a URL, strip any query string before getting extension
$qpos = strpos($filename, '?');
if (false !== $qpos) {
$filename = substr($filename, 0, $qpos);
}
$pathinfo = self::mb_pathinfo($filename);
return self::_mime_types($pathinfo['extension']);
}
/**
* Multi-byte-safe pathinfo replacement.
* Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
* Works similarly to the one in PHP >= 5.2.0
* @link http://www.php.net/manual/en/function.pathinfo.php#107461
* @param string $path A filename or path, does not need to exist as a file
* @param integer|string $options Either a PATHINFO_* constant,
* or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
* @return string|array
* @static
*/
public static function mb_pathinfo($path, $options = null)
{
$ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
$pathinfo = array();
if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
if (array_key_exists(1, $pathinfo)) {
$ret['dirname'] = $pathinfo[1];
}
if (array_key_exists(2, $pathinfo)) {
$ret['basename'] = $pathinfo[2];
}
if (array_key_exists(5, $pathinfo)) {
$ret['extension'] = $pathinfo[5];
}
if (array_key_exists(3, $pathinfo)) {
$ret['filename'] = $pathinfo[3];
}
}
switch ($options) {
case PATHINFO_DIRNAME:
case 'dirname':
return $ret['dirname'];
case PATHINFO_BASENAME:
case 'basename':
return $ret['basename'];
case PATHINFO_EXTENSION:
case 'extension':
return $ret['extension'];
case PATHINFO_FILENAME:
case 'filename':
return $ret['filename'];
default:
return $ret;
}
}
/**
* Set or reset instance properties.
* You should avoid this function - it's more verbose, less efficient, more error-prone and
* harder to debug than setting properties directly.
* Usage Example:
* `$mail->set('SMTPSecure', 'tls');`
* is the same as:
* `$mail->SMTPSecure = 'tls';`
* @access public
* @param string $name The property name to set
* @param mixed $value The value to set the property to
* @return boolean
* @TODO Should this not be using the __set() magic function?
*/
public function set($name, $value = '')
{
if (property_exists($this, $name)) {
$this->$name = $value;
return true;
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
$this->setError($this->lang('variable_set') . $name);
return false;
}
}
/**
* Strip newlines to prevent header injection.
* @access public
* @param string $str
* @return string
*/
public function secureHeader($str)
{
return trim(str_replace(array("\r", "\n"), '', $str));
}
/**
* Normalize line breaks in a string.
* Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
* Defaults to CRLF (for message bodies) and preserves consecutive breaks.
* @param string $text
* @param string $breaktype What kind of line break to use, defaults to CRLF
* @return string
* @access public
* @static
*/
public static function normalizeBreaks($text, $breaktype = "\r\n")
{
return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
}
/**
* Set the public and private key files and password for S/MIME signing.
* @access public
* @param string $cert_filename
* @param string $key_filename
* @param string $key_pass Password for private key
* @param string $extracerts_filename Optional path to chain certificate
*/
public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
{
$this->sign_cert_file = $cert_filename;
$this->sign_key_file = $key_filename;
$this->sign_key_pass = $key_pass;
$this->sign_extracerts_file = $extracerts_filename;
}
/**
* Quoted-Printable-encode a DKIM header.
* @access public
* @param string $txt
* @return string
*/
public function DKIM_QP($txt)
{
$line = '';
for ($i = 0; $i < strlen($txt); $i++) {
$ord = ord($txt[$i]);
if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
$line .= $txt[$i];
} else {
$line .= '=' . sprintf('%02X', $ord);
}
}
return $line;
}
/**
* Generate a DKIM signature.
* @access public
* @param string $signHeader
* @throws phpmailerException
* @return string The DKIM signature value
*/
public function DKIM_Sign($signHeader)
{
if (!defined('PKCS7_TEXT')) {
if ($this->exceptions) {
throw new phpmailerException($this->lang('extension_missing') . 'openssl');
}
}
$privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private);
if ('' != $this->DKIM_passphrase) {
$privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
$privKey = openssl_pkey_get_private($privKeyStr);
}
//Workaround for missing digest algorithms in old PHP & OpenSSL versions
//@link http://stackoverflow.com/a/11117338/333340
if (version_compare(PHP_VERSION, '5.3.0') >= 0 and
in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) {
if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
openssl_pkey_free($privKey);
return base64_encode($signature);
}
$pinfo = openssl_pkey_get_details($privKey);
$hash = hash('sha256', $signHeader);
//'Magic' constant for SHA256 from RFC3447
//@link https://tools.ietf.org/html/rfc3447#page-43
$t = '3031300d060960864801650304020105000420' . $hash;
$pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3);
$eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t);
if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) {
openssl_pkey_free($privKey);
return base64_encode($signature);
}
}
openssl_pkey_free($privKey);
return '';
}
/**
* Generate a DKIM canonicalization header.
* @access public
* @param string $signHeader Header
* @return string
*/
public function DKIM_HeaderC($signHeader)
{
$signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
$lines = explode("\r\n", $signHeader);
foreach ($lines as $key => $line) {
list($heading, $value) = explode(':', $line, 2);
$heading = strtolower($heading);
$value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces
$lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
}
$signHeader = implode("\r\n", $lines);
return $signHeader;
}
/**
* Generate a DKIM canonicalization body.
* @access public
* @param string $body Message Body
public function DKIM_BodyC($body)
{
if ($body == '') {
return "\r\n";
// stabilize line endings
$body = str_replace("\r\n", "\n", $body);
$body = str_replace("\n", "\r\n", $body);
// END stabilize line endings
while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
$body = substr($body, 0, strlen($body) - 2);
}
return $body;
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
/**
* Create the DKIM header and body in a new message header.
* @access public
* @param string $headers_line Header lines
* @param string $subject Subject
* @param string $body Body
* @return string
*/
public function DKIM_Add($headers_line, $subject, $body)
{
$DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
$DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
$DKIMquery = 'dns/txt'; // Query method
$DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
$subject_header = "Subject: $subject";
$headers = explode($this->LE, $headers_line);
$from_header = '';
$to_header = '';
$date_header = '';
$current = '';
foreach ($headers as $header) {
if (strpos($header, 'From:') === 0) {
$from_header = $header;
$current = 'from_header';
} elseif (strpos($header, 'To:') === 0) {
$to_header = $header;
$current = 'to_header';
} elseif (strpos($header, 'Date:') === 0) {
$date_header = $header;
$current = 'date_header';
} else {
if (!empty($$current) && strpos($header, ' =?') === 0) {
$$current .= $header;
} else {
$current = '';
}
}
}
$from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
$to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
$date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
$subject = str_replace(
'|',
'=7C',
$this->DKIM_QP($subject_header)
); // Copied header fields (dkim-quoted-printable)
$body = $this->DKIM_BodyC($body);
$DKIMlen = strlen($body); // Length of body
$DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
if ('' == $this->DKIM_identity) {
$ident = '';
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
$ident = ' i=' . $this->DKIM_identity . ';';
}
$dkimhdrs = 'DKIM-Signature: v=1; a=' .
$DKIMsignatureType . '; q=' .
$DKIMquery . '; l=' .
$DKIMlen . '; s=' .
$this->DKIM_selector .
";\r\n" .
"\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
"\th=From:To:Date:Subject;\r\n" .
"\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
"\tz=$from\r\n" .
"\t|$to\r\n" .
"\t|$date\r\n" .
"\t|$subject;\r\n" .
"\tbh=" . $DKIMb64 . ";\r\n" .
"\tb=";
$toSign = $this->DKIM_HeaderC(
$from_header . "\r\n" .
$to_header . "\r\n" .
$date_header . "\r\n" .
$subject_header . "\r\n" .
$dkimhdrs
);
$signed = $this->DKIM_Sign($toSign);
return $dkimhdrs . $signed . "\r\n";
}
/**
* Detect if a string contains a line longer than the maximum line length allowed.
* @param string $str
* @return boolean
* @static
*/
public static function hasLineLongerThanMax($str)
{
//+2 to include CRLF line break for a 1000 total
return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
}
/**
* Allows for public read access to 'to' property.
* @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
* @access public
* @return array
*/
public function getToAddresses()
{
return $this->to;
}
/**
* Allows for public read access to 'cc' property.
* @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
* @access public
* @return array
*/
public function getCcAddresses()
{
return $this->cc;
}
/**
* Allows for public read access to 'bcc' property.
* @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
* @access public
* @return array