diff --git a/dev/examples/mail/source_email_ticket_2_answer_from_ticket.txt b/dev/examples/mail/source_email_ticket_2_answer_from_ticket.txt index 350b9a93cada3..969d4eec95a3c 100644 --- a/dev/examples/mail/source_email_ticket_2_answer_from_ticket.txt +++ b/dev/examples/mail/source_email_ticket_2_answer_from_ticket.txt @@ -10,7 +10,8 @@ To: customer@customercompany.fr Subject: [MyBigCompany - Ticket #TS2008-0040] Nouveau message Date: Thu, 20 Aug 2020 18:31:37 +0200 Message-ID: <1597941097.SMTPs-dolibarr-tic58@83b5bc91f83a56e458db71e0adac2b62> -References: <1597941097.SMTPs-dolibarr-tic58@83b5bc91f83a56e458db71e0adac2b62> +References: +In-Reply-To: X-Dolibarr-TRACKID: tic58@83b5bc91f83a56e458db71e0adac2b62 X-RemoteAddr: 127.0.0.1 X-Mailer: Dolibarr version 13.0.0-alpha (using SMTPs Mailer) @@ -25,17 +26,17 @@ Content-Type: multipart/alternative; boundary="mul_872cdd6a64216735955664484832b --mul_872cdd6a64216735955664484832b075 Content-Type: text/plain; charset=UTF-8 -Bonjour +Bonjour + - Une nouvelle réponse a été ajoutée à un ticket que vous suivez. Voici -le message :PredefinedMailContentTicket_send - - +le message :PredefinedMailContentTicket_send + + Vous pouvez voir la progression du ticket en cliquant sur le lien -ci-dessus. : fr5uw2yospypn2rz - Cordialement, - +ci-dessus. : fr5uw2yospypn2rz + Cordialement, + -- --mul_872cdd6a64216735955664484832b075 Content-Type: text/html; charset=UTF-8 diff --git a/htdocs/core/class/CMailFile.class.php b/htdocs/core/class/CMailFile.class.php index 9d73998e151bf..323b544c9bae5 100644 --- a/htdocs/core/class/CMailFile.class.php +++ b/htdocs/core/class/CMailFile.class.php @@ -52,7 +52,7 @@ class CMailFile public $subject; public $addr_from; // From: Label and EMail of sender (must include '<>'). For example '' or 'John Doe ' or ''). Note that with gmail smtps, value here is forced by google to account (but not the reply-to). // Sender: Who send the email ("Sender" has sent emails on behalf of "From"). - // Use it when the "From" is an email of a domain that is a SPF protected domain, and sending smtp server is not this domain. In such case, add Sender field with an email of the protected domain. + // Use it when the "From" is an email of a domain that is a SPF protected domain, and the sending smtp server is not this domain. In such case, add Sender field with an email of the protected domain. // Return-Path: Email where to send bounds. public $reply_to; // Reply-To: Email where to send replies from mailer software (mailer use From if reply-to not defined, Gmail use gmail account if reply-to not defined) public $errors_to; // Errors-To: Email where to send errors. @@ -114,7 +114,19 @@ class CMailFile * @var string Message-ID of the email to send (generated) */ public $msgid; + + /** + * @var string Value to use in In-reply-to when email is set as an answer of another email (The Msg-Id of received email) + */ + public $in_reply_to; + + /** + * @var string References to add to the email to send (generated from the email we answer) + */ + public $references; + public $headers; + public $message; /** @@ -172,10 +184,12 @@ class CMailFile * @param string $trackid Tracking string (contains type and id of related element) * @param string $moreinheader More in header. $moreinheader must contains the "\r\n" at end of each line * @param string $sendcontext 'standard', 'emailing', 'ticket', 'password', ... (used to define which sending mode and parameters to use) - * @param string $replyto Reply-to email (will be set to same value than From by default if not provided) + * @param string $replyto Reply-to email (will be set to the same value than From by default if not provided) * @param string $upload_dir_tmp Temporary directory (used to convert images embedded as img src=data:image) + * @param string $in_reply_to Message-ID of the message we reply Token + * @param string $references String with list of Message-ID of the thread ('<123> <456> ...') */ - public function __construct($subject, $to, $from, $msg, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $addr_cc = "", $addr_bcc = "", $deliveryreceipt = 0, $msgishtml = 0, $errors_to = '', $css = '', $trackid = '', $moreinheader = '', $sendcontext = 'standard', $replyto = '', $upload_dir_tmp = '') + public function __construct($subject, $to, $from, $msg, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $addr_cc = "", $addr_bcc = "", $deliveryreceipt = 0, $msgishtml = 0, $errors_to = '', $css = '', $trackid = '', $moreinheader = '', $sendcontext = 'standard', $replyto = '', $upload_dir_tmp = '', $in_reply_to = '', $references = '') { global $conf, $dolibarr_main_data_root, $user; @@ -428,6 +442,8 @@ public function __construct($subject, $to, $from, $msg, $filename_list = array() $this->reply_to = dol_sanitizeEmail($replyto); $this->errors_to = dol_sanitizeEmail($errors_to); $this->trackid = $trackid; + $this->in_reply_to = $in_reply_to; + $this->references = $references; // Set arrays with attached files info $this->filename_list = $filename_list; $this->mimetype_list = $mimetype_list; @@ -462,7 +478,7 @@ public function __construct($subject, $to, $from, $msg, $filename_list = array() $text_body = ""; $files_encoded = ""; - // Define smtp_headers (this also set ->msgid) + // Define smtp_headers (this also set SMTP headers from ->msgid, ->in_reply_to and ->references) $smtp_headers = $this->write_smtpheaders(); if (!empty($moreinheader)) { $smtp_headers .= $moreinheader; // $moreinheader contains the \r\n @@ -516,15 +532,23 @@ public function __construct($subject, $to, $from, $msg, $filename_list = array() $smtps->setSubject($subjecttouse); $smtps->setTO($this->getValidAddress($this->addr_to, 0, 1)); $smtps->setFrom($this->getValidAddress($this->addr_from, 0, 1)); - $smtps->setTrackId($this->trackid); $smtps->setReplyTo($this->getValidAddress($this->reply_to, 0, 1)); - //X-Dolibarr-TRACKID is generated inside the smtps->getHeader + $smtps->setTrackId($this->trackid); + + if (!empty($this->in_reply_to)) { + $smtps->setInReplyTo($this->in_reply_to); + } + if (!empty($this->references)) { + $smtps->setReferences($this->references); + } if (!empty($moreinheader)) { $smtps->setMoreInHeader($moreinheader); } + //X-Dolibarr-TRACKID, In-Reply-To, References and $moreinheader will be added to header inside the smtps->getHeader + if (!empty($this->html)) { if (!empty($css)) { $this->css = $css; @@ -592,8 +616,14 @@ public function __construct($subject, $to, $from, $msg, $filename_list = array() $msgid = $headers->get('Message-ID'); $msgid->setId($headerID); + // Add 'In-Reply-To:' header + if (!empty($this->in_reply_to)) { + $headers->addIdHeader('In-Reply-To', $this->in_reply_to); + } // Add 'References:' header - //$headers->addIdHeader('References', $headerID); + if (!empty($this->references)) { + $headers->addIdHeader('References', $this->references); + } if (!empty($moreinheader)) { $moreinheaderarray = preg_split('/[\r\n]+/', $moreinheader); @@ -1543,16 +1573,23 @@ public function write_smtpheaders() $trackid = $this->trackid; if ($trackid) { - // References is kept in response and Message-ID is returned into In-Reply-To: $this->msgid = time().'.phpmail-dolibarr-'.$trackid.'@'.$host; $out .= 'Message-ID: <'.$this->msgid.">".$this->eol2; // Uppercase seems replaced by phpmail - //$out .= 'References: <'.$this->msgid.">".$this->eol2; $out .= 'X-Dolibarr-TRACKID: '.$trackid.'@'.$host.$this->eol2; } else { $this->msgid = time().'.phpmail@'.$host; $out .= 'Message-ID: <'.$this->msgid.">".$this->eol2; } + // Add 'In-Reply-To:' header with the Message-Id we answer + if (!empty($this->in_reply_to)) { + $out .= 'In-Reply-To: <'.$this->in_reply_to.'>'.$this->eol2; + } + // Add 'References:' header with list of all Message-ID in thread history + if (!empty($this->references)) { + $out .= 'References: '.$this->references.$this->eol2; + } + if (!empty($_SERVER['REMOTE_ADDR'])) { $out .= "X-RemoteAddr: ".$_SERVER['REMOTE_ADDR'].$this->eol2; } diff --git a/htdocs/core/class/smtps.class.php b/htdocs/core/class/smtps.class.php index bca65ed5d84fd..6b9a2a272f489 100644 --- a/htdocs/core/class/smtps.class.php +++ b/htdocs/core/class/smtps.class.php @@ -4,7 +4,7 @@ * Copyright (C) 2005-2015 Laurent Destailleur * Copyright (C) 2006-2011 Regis Houssin * Copyright (C) 2016 Jonathan TISSEAU - * Copyright (C) 2024 MDW + * Copyright (C) 2024 MDW * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -88,6 +88,16 @@ class SMTPs */ private $_msgReplyTo = null; + /** + * List of In-Reply-To + */ + private $_msgInReplyTo = null; + + /** + * List of Msg-Id + */ + private $_msgReferences = null; + /** * Who will the Message be sent to; TO, CC, BCC * Multi-diminsional array containing addresses the message will @@ -1139,6 +1149,56 @@ public function getReplyTo($_part = true) return $_retValue; } + /** + * Set References in the list of Msg-Id + * + * @param string $_strInReplyTo List of Msg-Id + * @return void + */ + public function setInReplyTo($_strInReplyTo) + { + if ($_strInReplyTo) { + $this->_msgInReplyTo = $_strInReplyTo; + } + } + + /** + * Retrieves the InReplyTo from which mail we reply to + * + * @return string Msg-Id of email we reply to + */ + public function getInReplyTo() + { + $_retValue = $this->_msgInReplyTo; + + return $_retValue; + } + + /** + * Set References in the list of Msg-Id + * + * @param string $_strReferences List of Msg-Id + * @return void + */ + public function setReferences($_strReferences) + { + if ($_strReferences) { + $this->_msgReferences = $_strReferences; + } + } + + /** + * Retrieves the References from which mail will be the reply-to + * + * @return string List of Msg-Id + */ + public function getReferences() + { + $_retValue = $this->_msgReferences; + + return $_retValue; + } + /** * Inserts given addresses into structured format. * This method takes a list of given addresses, via an array or a COMMA delimited string, and inserts them into a highly @@ -1453,8 +1513,6 @@ public function getHeader() if ($trackid) { $_header .= 'Message-ID: <'.time().'.SMTPs-dolibarr-'.$trackid.'@'.$host.">\r\n"; $_header .= 'X-Dolibarr-TRACKID: '.$trackid.'@'.$host."\r\n"; - // References and In-Reply-To: will be set by caller - //$_header .= 'References: <'.time().'.SMTPs-dolibarr-'.$trackid.'@'.$host.">\r\n"; } else { $_header .= 'Message-ID: <'.time().'.SMTPs@'.$host.">\r\n"; } @@ -1489,7 +1547,13 @@ public function getHeader() $_header .= 'X-Dolibarr-Option: '.($conf->global->MAIN_MAIL_USE_MULTI_PART ? 'MAIN_MAIL_USE_MULTI_PART' : 'No MAIN_MAIL_USE_MULTI_PART')."\r\n"; $_header .= 'Mime-Version: 1.0'."\r\n"; - // TODO Add also $this->references and In-Reply-To + // Add also $this->references and In-Reply-To + if ($this->getInReplyTo()) { + $_header .= "In-Reply-To: ".$this->getInReplyTo()."\r\n"; + } + if ($this->getReferences()) { + $_header .= "References: ".$this->getReferences()."\r\n"; + } return $_header; } diff --git a/htdocs/ticket/class/ticket.class.php b/htdocs/ticket/class/ticket.class.php index 6f01756cb165c..d7927cc86df52 100644 --- a/htdocs/ticket/class/ticket.class.php +++ b/htdocs/ticket/class/ticket.class.php @@ -3020,20 +3020,22 @@ public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = if (!empty($this->email_msgid)) { // We must also add 1 entry In-Reply-To: <$this->email_msgid> with Message-ID we respond from (See RFC5322). $moreinheader .= 'In-Reply-To: <'.$this->email_msgid.'>'."\r\n"; + // TODO We should now be able to give the in_reply_to as a dedicated parameter of new CMailFile() instead of into $moreinheader. } // We should add here also a header 'References:' // According to RFC5322, we should add here all the References fields of the initial message concatenated with // the Message-ID of the message we respond from (but each ID must be once). $references = ''; - if (empty($this->origin_references)) { - // TODO If No References is set, use the In-Reply-To for $references .= (empty($references) ? '' : ' ').Source In-reply-To - } else { + if (!empty($this->origin_references)) { // $this->origin_references should be '<'.$this->origin_references.'>' $references .= (empty($references) ? '' : ' ').$this->origin_references; } - $references .= (empty($references) ? '' : ' ').'<'.$this->email_msgid.'>'; + if (!empty($this->email_msgid) && !preg_match('/'.preg_quote('/', $this->email_msgid).'/', $references)) { + $references .= (empty($references) ? '' : ' ').'<'.$this->email_msgid.'>'; + } if ($references) { $moreinheader .= 'References: '.$references."\r\n"; + // TODO We should now be able to give the references as a dedicated parameter of new CMailFile() instead of into $moreinheader. } $mailfile = new CMailFile($subject, $receiver, $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', $trackid, $moreinheader, 'ticket', '', $upload_dir_tmp);