105 private $_msgContent = array();
130 'Company Confidential');
233 public $lastretval =
'';
250 private $_errorsTo =
'';
251 private $_deliveryReceipt = 0;
252 private $_trackId =
'';
253 private $_moreinheader =
'';
279 $this->_deliveryReceipt = $_val;
289 return $this->_deliveryReceipt;
300 $this->_trackId = $_val;
311 $this->_moreinheader = $_val;
321 return $this->_trackId;
331 return $this->_moreinheader;
357 if ($_part ===
true) {
358 $_retValue = $this->_errorsTo;
360 $_retValue = $this->_errorsTo[$_part];
374 $this->_debug = $_vDebug;
385 $_aryToList = $this->getTO();
404 $usetls = preg_match(
'@tls://@i', $host);
406 $host = preg_replace(
'@tcp://@i',
'', $host);
407 $host = preg_replace(
'@ssl://@i',
'', $host);
408 $host = preg_replace(
'@tls://@i',
'', $host);
411 include_once DOL_DOCUMENT_ROOT.
'/core/lib/functions2.lib.php';
413 if ((!
is_ip($host)) && ((gethostbyname($host)) == $host)) {
414 $this->
_setErr(99, $host.
' is either offline or is an invalid host name.');
417 if (function_exists(
'stream_socket_client') && !empty($this->_options)) {
418 $socket_context = stream_context_create($this->_options);
419 $this->socket = @stream_socket_client(
420 preg_replace(
'@tls://@i',
'', $this->
getHost()).
425 STREAM_CLIENT_CONNECT,
429 $this->socket = @fsockopen(
430 preg_replace(
'@tls://@i',
'', $this->
getHost()),
439 if (is_resource($this->socket)) {
444 if (function_exists(
'stream_set_timeout')) {
445 stream_set_timeout($this->socket, $this->_smtpTimeout, 0);
449 if ($_retVal = $this->
server_parse($this->socket,
"220")) {
450 $_retVal = $this->socket;
455 if (empty($this->errstr)) {
456 $this->errstr =
'Failed to connect with fsockopen host='.$this->getHost().
' port='.$this->
getPort();
458 $this->
_setErr($this->errno, $this->errstr);
477 require_once DOL_DOCUMENT_ROOT.
'/core/lib/geturl.lib.php';
482 $usetls = preg_match(
'@tls://@i', $host);
484 $host = preg_replace(
'@tcp://@i',
'', $host);
485 $host = preg_replace(
'@ssl://@i',
'', $host);
486 $host = preg_replace(
'@tls://@i',
'', $host);
488 if ($usetls && !empty($conf->global->MAIN_SMTPS_ADD_TLS_TO_HOST_FOR_HELO)) {
489 $host =
'tls://'.$host;
494 if (!empty($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO)) {
495 if (!is_numeric($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO)) {
497 $hosth = $conf->global->MAIL_SMTP_USE_FROM_FOR_HELO;
498 } elseif ($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO == 1) {
501 $hosth = $this->
getFrom(
'addr');
502 $hosth = preg_replace(
'/^.*</',
'', $hosth);
503 $hosth = preg_replace(
'/>.*$/',
'', $hosth);
504 $hosth = preg_replace(
'/.*@/',
'', $hosth);
505 } elseif ($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO == 2) {
507 global $dolibarr_main_url_root;
567 $this->
_setErr(131,
'STARTTLS connection is not supported.');
578 $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
579 if (defined(
'STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
580 $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
581 $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
584 if (!stream_socket_enable_crypto($this->socket,
true, $crypto_method)) {
585 $this->
_setErr(132,
'STARTTLS connection failed.');
591 $this->
_setErr(126,
'"'.$hosth.
'" does not support authenticated connections or temporary error. Error after 2nd sending EHLO '.$hosth.
' : '.$this->lastretval);
597 if (empty($conf->global->MAIN_MAIL_SMTPS_AUTH_TYPE)) {
598 $conf->global->MAIN_MAIL_SMTPS_AUTH_TYPE =
'LOGIN';
603 switch ($conf->global->MAIN_MAIL_SMTPS_AUTH_TYPE) {
611 $_retVal = $this->
socket_send_str(base64_encode(
"\0".$this->_smtpsID.
"\0".$this->_smtpsPW),
'235');
617 $initRes =
"user=".$user.
"\001auth=Bearer ".$token.
"\001\001";
618 $_retVal = $this->
socket_send_str(
'AUTH XOAUTH2 '.base64_encode($initRes),
'235');
620 $this->
_setErr(130,
'Error when asking for AUTH XOAUTH2');
627 $this->
_setErr(130,
'Error when asking for AUTH LOGIN');
633 $_retVal = $this->
socket_send_str(base64_encode($this->_smtpsPW),
'235');
638 $this->
_setErr(130,
'Invalid Authentication Credentials.');
641 $this->
_setErr(126,
'"'.$host.
'" does not support authenticated connections or temporary error. Error after sending EHLO '.$hosth.
' : '.$this->lastretval);
664 if (!empty($this->_smtpsID) && (!empty($this->_smtpsPW) || !empty($this->_smtpsToken))) {
671 $usetls = preg_match(
'@tls://@i', $host);
673 $host = preg_replace(
'@tcp://@i',
'', $host);
674 $host = preg_replace(
'@ssl://@i',
'', $host);
675 $host = preg_replace(
'@tls://@i',
'', $host);
677 if ($usetls && !empty($conf->global->MAIN_SMTPS_ADD_TLS_TO_HOST_FOR_HELO)) {
678 $host =
'tls://'.$host;
683 if (!empty($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO)) {
684 if (!is_numeric($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO)) {
686 $hosth = $conf->global->MAIL_SMTP_USE_FROM_FOR_HELO;
687 } elseif ($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO == 1) {
690 $hosth = $this->
getFrom(
'addr');
691 $hosth = preg_replace(
'/^.*</',
'', $hosth);
692 $hosth = preg_replace(
'/>.*$/',
'', $hosth);
693 $hosth = preg_replace(
'/.*@/',
'', $hosth);
694 } elseif ($conf->global->MAIL_SMTP_USE_FROM_FOR_HELO == 2) {
696 global $dolibarr_main_url_root;
711 if (!$resultmailfrom) {
712 fclose($this->socket);
747 fputs($this->socket,
'QUIT');
752 fclose($this->socket);
797 if (!empty($_strConfigPath)) {
800 if (!@include $_strConfigPath) {
801 $this->
_setErr(110,
'"'.$_strConfigPath.
'" is not a valid path.');
808 if ($_host = ini_get(
'SMTPs')) {
812 if ($_port = ini_get(
'smtp_port')) {
816 if ($_from = ini_get(
'sendmail_from')) {
836 if ((is_numeric($_type)) && (($_type >= 0) && ($_type <= 3))) {
837 $this->_transportType = $_type;
880 $this->_smtpsHost = $_strHost;
905 if ((is_numeric($_intPort)) &&
906 (($_intPort >= 1) && ($_intPort <= 65536))) {
907 $this->_smtpsPort = $_intPort;
930 $this->_smtpsID = $_strID;
951 $this->_smtpsPW = $_strPW;
972 $this->_smtpsToken = $_strToken;
995 $this->_smtpsCharSet = $_strCharSet;
1026 if (array_search($_strTransEncode, $this->_smtpsTransEncodeTypes)) {
1027 $this->_smtpsTransEncode = $_strTransEncode;
1059 if (array_search($_strTransEncodeType, $this->_smtpsTransEncodeTypes)) {
1060 $this->_smtpsTransEncodeType = $_strTransEncodeType;
1100 if ($_part ===
true) {
1103 $_retValue = $this->_msgFrom[$_part];
1118 $this->_msgReplyTo = $this->
_strip_email($_strReplyTo);
1132 if ($_part ===
true) {
1135 $_retValue = $this->_msgReplyTo[$_part];
1157 if (!empty($_addrList)) {
1159 if (is_string($_addrList)) {
1161 if (strstr($_addrList,
',')) {
1163 $_addrList = explode(
',', $_addrList);
1166 $_addrList = array($_addrList);
1171 foreach ($_addrList as $_strAddr) {
1173 $_strAddr = str_replace(
'>',
'', $_strAddr);
1177 $_tmpaddr = explode(
'<', $_strAddr);
1180 if (count($_tmpaddr) == 2) {
1181 $_tmpHost = explode(
'@', $_tmpaddr[1]);
1182 $_tmpaddr[0] = trim($_tmpaddr[0],
' ">');
1183 $aryHost[$_tmpHost[1]][$_type][$_tmpHost[0]] = $_tmpaddr[0];
1187 $_strAddr = str_replace(
'<',
'', $_strAddr);
1189 $_tmpHost = explode(
'@', $_strAddr);
1190 $_tmpHost[0] = trim($_tmpHost[0]);
1191 $_tmpHost[1] = trim($_tmpHost[1]);
1193 $aryHost[$_tmpHost[1]][$_type][$_tmpHost[0]] =
'';
1198 $this->_msgRecipients = $aryHost;
1222 $_aryEmail[
'org'] = $_strAddr;
1225 $_strAddr = strtolower($_strAddr);
1228 $_strAddr = trim($_strAddr,
' ">');
1231 $_tmpAry = explode(
'<', $_strAddr);
1234 if (count($_tmpAry) == 2) {
1237 $_aryEmail[
'real'] = trim($_tmpAry[0],
' ">');
1240 $_aryEmail[
'addr'] = $_tmpAry[1];
1242 $_aryEmail[
'addr'] = $_tmpAry[0];
1246 $_tmpHost = explode(
'@', $_aryEmail[
'addr']);
1247 $_aryEmail[
'user'] = $_tmpHost[0];
1248 $_aryEmail[
'host'] = $_tmpHost[1];
1251 $_aryEmail[
'addr'] =
'<'.$_aryEmail[
'addr'].
'>';
1270 $_RCPT_list = array();
1273 foreach ($this->_msgRecipients as $_host => $_list) {
1274 foreach ($_list as $_subList) {
1275 foreach ($_subList as $_name => $_addr) {
1277 $_RCPT_list[] = $_name.
'@'.$_host;
1298 if ($this->_msgRecipients) {
1299 $_RCPT_list = array();
1301 foreach ($this->_msgRecipients as $_host => $_list) {
1302 if (!empty($this->_msgRecipients[$_host][$_which])) {
1303 foreach ($this->_msgRecipients[$_host][$_which] as $_addr => $_realName) {
1305 $_realName =
'"'.$_realName.
'"';
1306 $_RCPT_list[] = $_realName.
' <'.$_addr.
'@'.$_host.
'>';
1308 $_RCPT_list[] = $_addr.
'@'.$_host;
1314 return implode(
', ', $_RCPT_list);
1316 $this->
_setErr(101,
'No eMail Address for message to be sent to.');
1320 $this->
_setErr(102,
'eMail type not defined.');
1403 $this->_msgSubject = $_strSubject;
1426 $_header =
'From: '.$this->getFrom(
'org').
"\r\n"
1427 .
'To: '.$this->getTO().
"\r\n";
1429 if ($this->
getCC()) {
1430 $_header .=
'Cc: '.$this->getCC().
"\r\n";
1444 $host = dol_getprefix(
'email');
1447 $_header .=
'Subject: '.$this->getSubject().
"\r\n";
1448 $_header .=
'Date: '.date(
"r").
"\r\n";
1453 $_header .=
'Message-ID: <'.time().
'.SMTPs-dolibarr-'.$trackid.
'@'.$host.
">\r\n";
1454 $_header .=
'References: <'.time().
'.SMTPs-dolibarr-'.$trackid.
'@'.$host.
">\r\n";
1455 $_header .=
'X-Dolibarr-TRACKID: '.$trackid.
'@'.$host.
"\r\n";
1457 $_header .=
'Message-ID: <'.time().
'.SMTPs@'.$host.
">\r\n";
1459 if (!empty($_SERVER[
'REMOTE_ADDR'])) {
1460 $_header .=
"X-RemoteAddr: ".$_SERVER[
'REMOTE_ADDR'].
"\r\n";
1471 $_header .=
'Sensitivity: '.$this->getSensitivity().
"\r\n";
1474 if ($this->_msgPriority != 3) {
1481 $_header .=
'Disposition-Notification-To: '.$this->getFrom(
'addr').
"\r\n";
1484 $_header .=
'Errors-To: '.$this->getErrorsTo(
'addr').
"\r\n";
1487 $_header .=
"Reply-To: ".$this->getReplyTo(
'addr').
"\r\n";
1490 $_header .=
'X-Mailer: Dolibarr version '.DOL_VERSION.
' (using SMTPs Mailer)'.
"\r\n";
1491 $_header .=
'X-Dolibarr-Option: '.($conf->global->MAIN_MAIL_USE_MULTI_PART ?
'MAIN_MAIL_USE_MULTI_PART' :
'No MAIN_MAIL_USE_MULTI_PART').
"\r\n";
1492 $_header .=
'Mime-Version: 1.0'.
"\r\n";
1509 if ($strType ==
'html') {
1510 $strMimeType =
'text/html';
1512 $strMimeType =
'text/plain';
1516 $strContent = preg_replace(
"/(?<!\r)\n/si",
"\r\n", $strContent);
1518 $strContentAltText =
'';
1519 if ($strType ==
'html') {
1521 $strContentAltText = preg_replace(
'/<head><title>.*<\/style><\/head>/',
'', $strContent);
1522 $strContentAltText = preg_replace(
"/<br\s*[^>]*>/",
" ", $strContentAltText);
1523 $strContentAltText = html_entity_decode(strip_tags($strContentAltText));
1524 $strContentAltText = trim(wordwrap($strContentAltText, 75,
"\r\n"));
1529 $strContent = rtrim(wordwrap($strContent, 75,
"\r\n"));
1531 $this->_msgContent[$strType] = array();
1533 $this->_msgContent[$strType][
'mimeType'] = $strMimeType;
1534 $this->_msgContent[$strType][
'data'] = $strContent;
1535 $this->_msgContent[$strType][
'dataText'] = $strContentAltText;
1538 $this->_msgContent[$strType][
'md5'] =
dol_hash($strContent, 3);
1556 $_types = array_keys($this->_msgContent);
1559 $keyCount = count($_types);
1562 if ($keyCount === 0) {
1563 die(
"Sorry, no content");
1564 } elseif ($keyCount === 1 && empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) {
1566 $_msgData = $this->_msgContent;
1567 $_msgData = $_msgData[$_types[0]];
1569 $content =
'Content-Type: '.$_msgData[
'mimeType'].
'; charset="'.$this->
getCharSet().
'"'.
"\r\n"
1570 .
'Content-Transfer-Encoding: '.$this->getTransEncodeType().
"\r\n"
1571 .
'Content-Disposition: inline'.
"\r\n"
1572 .
'Content-Description: Message'.
"\r\n";
1575 $content .=
'Content-MD5: '.$_msgData[
'md5'].
"\r\n";
1579 . $_msgData[
'data'].
"\r\n";
1580 } elseif ($keyCount >= 1 || !empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) {
1587 $content =
'Content-Type: multipart/mixed; boundary="'.$this->_getBoundary(
'mixed').
'"'.
"\r\n";
1591 $content .=
"Content-Transfer-Encoding: 8bit\r\n";
1594 $content .=
"--".$this->_getBoundary(
'mixed').
"\r\n";
1596 if (key_exists(
'image', $this->_msgContent)) {
1597 $content .=
'Content-Type: multipart/alternative; boundary="'.$this->_getBoundary(
'alternative').
'"'.
"\r\n";
1599 $content .=
"--".$this->_getBoundary(
'alternative').
"\r\n";
1607 foreach ($this->_msgContent as $type => $_content) {
1608 if ($type ==
'attachment') {
1610 foreach ($_content as $_file => $_data) {
1611 $content .=
"--".$this->_getBoundary(
'mixed').
"\r\n"
1612 .
'Content-Disposition: attachment; filename="'.$_data[
'fileName'].
'"'.
"\r\n"
1613 .
'Content-Type: '.$_data[
'mimeType'].
'; name="'.$_data[
'fileName'].
'"'.
"\r\n"
1614 .
'Content-Transfer-Encoding: base64'.
"\r\n"
1615 .
'Content-Description: '.$_data[
'fileName'].
"\r\n";
1616 if (!empty($_data[
'cid'])) {
1617 $content .=
"X-Attachment-Id: ".$_data[
'cid'].
"\r\n";
1618 $content .=
"Content-ID: <".$_data[
'cid'].
">\r\n";
1621 $content .=
'Content-MD5: '.$_data[
'md5'].
"\r\n";
1624 $content .=
"\r\n".$_data[
'data'].
"\r\n\r\n";
1626 } elseif ($type ==
'image') {
1629 foreach ($_content as $_image => $_data) {
1630 $content .=
"--".$this->_getBoundary(
'related').
"\r\n";
1632 $content .=
'Content-Type: '.$_data[
'mimeType'].
'; name="'.$_data[
'imageName'].
'"'.
"\r\n"
1633 .
'Content-Transfer-Encoding: base64'.
"\r\n"
1634 .
'Content-Disposition: inline; filename="'.$_data[
'imageName'].
'"'.
"\r\n"
1635 .
'Content-ID: <'.$_data[
'cid'].
'> '.
"\r\n";
1638 $content .=
'Content-MD5: '.$_data[
'md5'].
"\r\n";
1642 . $_data[
'data'].
"\r\n";
1646 $content .=
"--".$this->_getBoundary(
'related').
"--\r\n";
1647 $content .=
"\r\n--".$this->_getBoundary(
'alternative').
"--\r\n";
1650 if (key_exists(
'image', $this->_msgContent)) {
1651 $content .=
"Content-Type: text/plain; charset=".$this->getCharSet().
"\r\n";
1652 $content .=
"\r\n".($_content[
'dataText'] ? $_content[
'dataText'] : strip_tags($_content[
'data'])).
"\r\n";
1653 $content .=
"--".$this->_getBoundary(
'alternative').
"\r\n";
1654 $content .=
'Content-Type: multipart/related; boundary="'.$this->_getBoundary(
'related').
'"'.
"\r\n";
1656 $content .=
"--".$this->_getBoundary(
'related').
"\r\n";
1659 if (!key_exists(
'image', $this->_msgContent) && $_content[
'dataText'] && !empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) {
1661 $content .=
'Content-Type: multipart/alternative; boundary="'.$this->_getBoundary(
'alternative').
'"'.
"\r\n";
1663 $content .=
"--".$this->_getBoundary(
'alternative').
"\r\n";
1665 $content .=
"Content-Type: text/plain; charset=".$this->getCharSet().
"\r\n";
1666 $content .=
"\r\n".$_content[
'dataText'].
"\r\n";
1667 $content .=
"--".$this->_getBoundary(
'alternative').
"\r\n";
1670 $content .=
'Content-Type: '.$_content[
'mimeType'].
'; charset='.$this->
getCharSet();
1675 $content .=
'Content-MD5: '.$_content[
'md5'].
"\r\n";
1678 $content .=
"\r\n".$_content[
'data'].
"\r\n";
1680 if (!key_exists(
'image', $this->_msgContent) && $_content[
'dataText'] && !empty($conf->global->MAIN_MAIL_USE_MULTI_PART)) {
1682 $content .=
"--".$this->_getBoundary(
'alternative').
"--\r\n";
1689 $content .=
"--".$this->_getBoundary(
'mixed').
'--'.
"\r\n";
1705 public function setAttachment($strContent, $strFileName =
'unknown', $strMimeType =
'unknown', $strCid =
'')
1708 $strContent = rtrim(chunk_split(base64_encode($strContent), 76,
"\r\n"));
1710 $this->_msgContent[
'attachment'][$strFileName][
'mimeType'] = $strMimeType;
1711 $this->_msgContent[
'attachment'][$strFileName][
'fileName'] = $strFileName;
1712 $this->_msgContent[
'attachment'][$strFileName][
'data'] = $strContent;
1713 $this->_msgContent[
'attachment'][$strFileName][
'cid'] = $strCid;
1716 $this->_msgContent[
'attachment'][$strFileName][
'md5'] =
dol_hash($strContent, 3);
1734 public function setImageInline($strContent, $strImageName =
'unknown', $strMimeType =
'unknown', $strImageCid =
'unknown')
1737 $this->_msgContent[
'image'][$strImageName][
'mimeType'] = $strMimeType;
1738 $this->_msgContent[
'image'][$strImageName][
'imageName'] = $strImageName;
1739 $this->_msgContent[
'image'][$strImageName][
'cid'] = $strImageCid;
1740 $this->_msgContent[
'image'][$strImageName][
'data'] = $strContent;
1743 $this->_msgContent[
'image'][$strImageName][
'md5'] =
dol_hash($strContent, 3);
1763 if ((is_numeric($_value)) &&
1764 (($_value >= 0) && ($_value <= 3))) {
1765 $this->_msgSensitivity = $_value;
1799 if ((is_numeric($_value)) &&
1800 (($_value >= 0) && ($_value <= 5))) {
1801 $this->_msgPriority = $_value;
1821 .
'X-Priority: '.$this->_msgPriority.
' ('.$this->_aryPriority[
$this->_msgPriority].
')'.
"\r\n";
1832 $this->_smtpMD5 = $_flag;
1856 $this->_msgXheader[] = $strXdata;
1877 $this->_smtpsBoundary =
"multipart_x.".time().
".x_boundary";
1878 $this->_smtpsRelatedBoundary =
'mul_'.dol_hash(uniqid(
"dolibarr2"), 3);
1879 $this->_smtpsAlternativeBoundary =
'mul_'.dol_hash(uniqid(
"dolibarr3"), 3);
1890 if ($type ==
'mixed') {
1892 } elseif ($type ==
'related') {
1894 } elseif ($type ==
'alternative') {
1920 $server_response =
'';
1925 while (substr($server_response, 3, 1) !=
' ' && $limit < 100) {
1926 if (!($server_response = fgets($socket, 256))) {
1927 $this->
_setErr(121,
"Couldn't get mail server response codes");
1931 $this->log .= $server_response;
1935 $this->lastretval = substr($server_response, 0, 3);
1937 if (!(substr($server_response, 0, 3) == $response)) {
1938 $this->
_setErr(120,
"Ran into problems sending Mail.\r\nResponse: ".$server_response);
1957 if ($this->_debug) {
1958 $this->log .= $_strSend;
1960 fputs($this->socket, $_strSend.$CRLF);
1961 if ($this->_debug) {
1962 $this->log .=
' ('.$_returnCode.
')'.$CRLF;
1966 return $this->
server_parse($this->socket, $_returnCode);
1984 $this->_smtpsErrors[] = array(
1999 if (is_array($this->_smtpsErrors)) {
2000 foreach ($this->_smtpsErrors as $_err => $_info) {
2001 $_errMsg[] =
'Error ['.$_info[
'num'].
']: '.$_info[
'msg'];
2005 return implode(
"\n", $_errMsg);