headers = [ 'X-Mailer' => MFAPPNAME.' Mailer', ]; $this->email_from = TT_OUTGOING_EMAIL; $this->from_name = TT_OUTGOING_EMAIL_NAME; if($object_type !== false) $this->object_type = $object_type; if($object_id !== false) $this->object_id = $object_id; if($object_data !== false) $this->object_data = $object_data; } public function addAttachment($filepath = null, $content = null, $name = false, $c_type = "application/octet-stream", $disposition = "attachment", $encoding = "base64" , $charset = "utf-8") { $attachment = [ "file" => $filepath, "content" => $content, "name" => $name, "c_type" => $c_type, "disposition" => $disposition, "encoding" => $encoding, "charset" => $charset ]; $attachment["isfile"] = false; if($filepath) { $attachment['isfile'] = true; } $this->attachments[] = $attachment; } public function setHeader($name, $value) { $this->headers[$name] = $value; } public function setSubject($subject) { $this->subject = $subject; } public function setBody($body) { $this->body = $body; } public function setHtmlBody($html) { $this->html = $html; } public function setFrom($email, $name = false) { $this->email_from = $email; $this->from_name = $name; } public function setTo($email) { $this->email_to = $email; } public function send() { if(!$this->email_to) { return false; } if(!$this->body && !$this->html) { return false; } if(!is_array($this->headers) || !count($this->headers)) { return false; } if(!$this->subject) { return false; } if(!array_key_exists("Subject", $this->headers) || !$this->headers['Subject']) { $this->setHeader("Subject", $this->subject); } if((!array_key_exists("From", $this->headers) || !$this->headers['From']) && $this->from_name) { $this->headers['From'] = '"'.$this->from_name.'" <'.$this->email_from.'>'; } $log = mfLoghandler::singleton(); //var_dump($this);exit; $mimeparams['text_encoding']="8bit"; $mimeparams['text_charset']="utf-8"; $mimeparams['html_charset']="utf-8"; $mimeparams['head_charset']="utf-8"; $mime = new Mail_mime(); if($this->body) { $mime->setTXTBody($this->body); } if($this->html) { $mime->setHTMLBody($this->html); } //var_dump($this->attachments);exit; if(count($this->attachments)) { foreach($this->attachments as $att) { if($att['isfile']) { $mime->addAttachment($att["file"], $att["c_type"], $att["name"], true, $att['encoding'], $att['disposition'], $att['charset']); } else { $mime->addAttachment($att["content"], $att["c_type"], $att["name"], false, $att['encoding'], $att['disposition'], $att['charset']); } } } $body = $mime->get($mimeparams); $headers = $mime->headers($this->headers); $this->_mimeheaders = $headers; //$log->debug(print_r($headers, true)); $mail =& Mail::factory('mail', ["-f ".$this->email_from]); /* * PHP 8's mail() function now outputs proper line endings in headers (CRLF instead of LF). * Mail_mail < 2.0 would detect a unix system and change the headers line ending to LF. * On Mail submission to Exim (via sendmail), mail() adds its own headers first with CRLF line ending, * making Exim treat every subsequent header's LF as an invalid line ending, adding a whitespace after the LF, * making all our custom headers into one long header. * * See: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-message_processing.html#SECTlineendings * * So we force CRLF line endings for headers here. */ $mail->sep = "\r\n"; $mail->send($this->email_to, $headers, $body); $this->_createEmailLog(); return true; } private function _createEmailLog() { if(!$this->object_id || !$this->object_type) return true; $log = mfLoghandler::singleton(); if(array_key_exists("To", $this->_mimeheaders)) { $this->_mimeheaders['To'] = $this->email_to; } FronkDB::singleton()->startTransaction(); $el = EmailLog::create([ 'object_type' => $this->object_type, 'object_id' => $this->object_id, 'object_data' => $this->object_data, 'from' => $this->email_from, 'to' => $this->email_to, 'headers' => json_encode($this->_mimeheaders), 'subject' => $this->subject, 'body_text' => $this->body, 'body_html' => $this->html, ]); if(!$el->save()) { $log->error(__METHOD__.": Error saving EmailLog. {$this->object_type}: {$this->object_id} from: '{$this->email_from}' | to: '{$this->email_to}' | subject: '{$this->subject}'"); FronkDB::singleton()->rollbackTransaction(); return false; } if(is_array($this->attachments) && count($this->attachments)) { foreach($this->attachments as $att) { if($att['isfile']) { if(!file_exists($att["file"])) { $log->error(__METHOD__.": File not found. ".$this->object_type.": ".$this->object_id." / Filename: ".$att['name']); continue; } $sha256_hash = hash_file("sha256", $att["file"]); $filesize = filesize($att["file"]); $file_content = file_get_contents($att["file"]); } else { $sha256_hash = hash("sha256", $att["content"]); $filesize = strlen($att["content"]); $file_content = $att["content"]; } // find attachment content or create it $ela_content = EmailLogAttachmentContent::getFirst(['sha256' => $sha256_hash]); if(!$ela_content) { $ela_content = EmailLogAttachmentContent::create([ 'mimetype' => $att['c_type'], 'filesize' => $filesize, 'sha256' => $sha256_hash, 'content' => $file_content, ]); if (!$ela_content->save()) { $log->error(__METHOD__ . ": Error saving EmailLogAttachmentContent. " . $this->object_type . ": " . $this->object_id . " / Filename: " . $att['name']); continue; } } $ela = EmailLogAttachment::create([ 'emaillog_id' => $el->id, 'content_id' => $ela_content->id, 'filename' => $att['name'], ]); if(!$ela->save()) { $log->error(__METHOD__.": Error saving EmailLogAttachment. ".$this->object_type.": ".$this->object_id." / Filename: ".$att['name']); } } } FronkDB::singleton()->commitTransaction(); } }