From 2b6cd1d21b842ab0dcc721d6af32537a52c97894 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sun, 5 Apr 2020 17:36:29 +0200 Subject: [PATCH] continue implementing mailing --- Message/Mail/DispositionType.php | 33 ++ Message/Mail/EmailAbstract.php | 82 ++++- Message/Mail/EncodingType.php | 34 ++ Message/Mail/EncryptionType.php | 31 ++ Message/Mail/ICALMethodType.php | 37 +++ Message/Mail/Imap.php | 8 + Message/Mail/Mail.php | 541 +++++++++++++++++++++++++++++-- Message/Mail/Pop3.php | 8 + Message/Mail/SubmitType.php | 33 ++ 9 files changed, 772 insertions(+), 35 deletions(-) create mode 100644 Message/Mail/DispositionType.php create mode 100644 Message/Mail/EncodingType.php create mode 100644 Message/Mail/EncryptionType.php create mode 100644 Message/Mail/ICALMethodType.php create mode 100644 Message/Mail/SubmitType.php diff --git a/Message/Mail/DispositionType.php b/Message/Mail/DispositionType.php new file mode 100644 index 000000000..3e4fbef5a --- /dev/null +++ b/Message/Mail/DispositionType.php @@ -0,0 +1,33 @@ +con === null ? false : \imap_ping($this->con); } + /** + * Set submit type/software + * + * @param string $submitType Submit type/software + * + * @return void + * + * @since 1.0.0 + */ + public function setSubmitType(string $submitType) : void + { + $this->submitType = $submitType; + + if ($this->submitType === SubmitType::SMTP || $this->submitType === SubmitType::MAIL) { + $this->endOfLine = $this->submitType === SubmitType::SMTP || !\stripos(\PHP_OS, 'WIN') === 0 ? \PHP_EOL : "\r\n"; + + return; + } elseif ($this->submitType === SubmitType::SENDMAIL) { + $this->endOfLine = \PHP_EOL; + $path = \ini_get('sendmail_path'); + + $this->sendmailPath = \stripos($path, 'sendmail') === false ? '/usr/sbin/sendmail' : $path; + } elseif ($this->submitType === SubmitType::QMAIL) { + $this->endOfLine = \PHP_EOL; + $path = \ini_get('sendmail_path'); + + $this->sendmailPath = \stripos($path, 'qmail') === false ? '/var/qmail/bin/qmail-inject' : $path; + } + } + + /** + * Send a Mail + * + * @param Mail $mail Mail to send + * + * @return bool + * + * @since 1.0.0 + */ + public function send(Mail $mail) : bool + { + if (empty($mail->getTo())) { + return false; + } + } + /** * Get boxes. * diff --git a/Message/Mail/EncodingType.php b/Message/Mail/EncodingType.php new file mode 100644 index 000000000..220f93338 --- /dev/null +++ b/Message/Mail/EncodingType.php @@ -0,0 +1,34 @@ +messageId = $id; + $this->id = $id; } /** @@ -194,6 +218,22 @@ class Mail $this->body = $body; } + /** + * Set body alt. + * + * @param string $body Mail body + * + * @return void + * + * @since 1.0.0 + */ + public function setBodyAlt(string $body) : void + { + $this->bodyAlt = $body; + $this->contentType = MimeType::M_ALT; + $this->setMessageType(); + } + /** * Set body. * @@ -221,4 +261,447 @@ class Mail { $this->encoding = $encoding; } + + /** + * Set content type. + * + * @param int $contentType Mail content type + * + * @return void + * + * @since 1.0.0 + */ + public function setContentType(int $contentType) : void + { + $this->contentType = empty($this->bodyAlt) ? $contentType : MimeType::M_ALT; + } + + /** + * Set subject + * + * @param string $subject Subject + * + * @return void + * + * @since 1.0.0 + */ + public function setSubject(string $subject) : void + { + $this->subject = $subject; + } + + /** + * Set the from address + * + * @param string $mail Mail address + * @param string $name Name + * + * @return bool + * + * @since 1.0.0 + */ + public function setFrom(string $mail, string $name = '') : bool + { + $mail = $this->normalizeEmailAddress($mail); + $name = $this->normalizeName($name); + + if ($mail === null) { + return false; + } + + $this->from = $mail; + $this->fromName = $name; + + return true; + } + + /** + * Add a to address + * + * @param string $mail Mail address + * @param string $name Name + * + * @return bool + * + * @since 1.0.0 + */ + public function addTo(string $mail, string $name = '') : bool + { + $mail = $this->normalizeEmailAddress($mail); + $name = \trim($name); + + if ($mail === null) { + return false; + } + + $this->to[$mail] = $name; + + return true; + } + + /** + * Get to addresses + * + * @return array + * + * @since 1.0.0 + */ + public function getTo() : array + { + return $this->to; + } + + /** + * Add a cc address + * + * @param string $mail Mail address + * @param string $name Name + * + * @return bool + * + * @since 1.0.0 + */ + public function addCc(string $mail, string $name = '') : bool + { + $mail = $this->normalizeEmailAddress($mail); + $name = \trim($name); + + if ($mail === null) { + return false; + } + + $this->cc[$mail] = $name; + + return true; + } + + /** + * Add a bcc address + * + * @param string $mail Mail address + * @param string $name Name + * + * @return bool + * + * @since 1.0.0 + */ + public function addBcc(string $mail, string $name = '') : bool + { + $mail = $this->normalizeEmailAddress($mail); + $name = \trim($name); + + if ($mail === null) { + return false; + } + + $this->bcc[$mail] = $name; + + return true; + } + + /** + * Add an attachment + * + * @param string $path Path to the file + * @param string $name Name of the file + * @param string $encoding Encoding + * @param string $type Mime type + * @param string $disposition Disposition + * + * @return bool + * + * @since 1.0.0 + */ + public function addAttachment( + string $path, + string $name = '', + string $encoding = EncodingType::E_BASE64, + string $type = '', + string $disposition = DispositionType::ATTACHMENT + ) : bool { + if ((bool) \preg_match('#^[a-z]+://#i', $path)) { + return false; + } + + $info = []; + \preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^.\\\\/]+?)|))[\\\\/.]*$#m', $path, $info); + $filename = $info[2] ?? ''; + + $this->attachment[] = [ + 'path' => $path, + 'filename' => $filename, + 'name' => $name, + 'encoding' => $encoding, + 'type' => $type, + 'disposition' => $disposition, + '???' => $name, + ]; + + $this->setMessageType(); + + return true; + } + + /** + * The email should be confirmed by the receivers + * + * @param string $confirm Should be confirmed? + * + * @return void + * + * @sicne 1.0.0 + */ + public function shouldBeConfirmed(bool $confirm = true) : void + { + $this->confirmReading = $confirm; + } + + /** + * Normalize an email address + * + * @param string $mail Mail address + * + * @return null|string + * + * @since 1.0.0 + */ + private function normalizeEmailAddress(string $mail) : ?string + { + $mail = \trim($mail); + $pos = \strrpos($mail, '@'); + + if ($pos === false || !\filter_var($mail, \FILTER_VALIDATE_EMAIL)) { + return null; + } + + $normalized = \idn_to_ascii($mail); + + return $normalized === false ? $mail : $normalized; + } + + /** + * Normalize an email name + * + * @param string $name Name + * + * @return string + * + * @since1 1.0.0 + */ + private function normalizeName(string $name) : string + { + return \trim(\preg_replace("/[\r\n]+/", '', $name)); + } + + /** + * Parsing an email containing a name + * + * @param string $mail Mail string + * + * @return array + * + * @since 1.0.0 + */ + private function parseEmailAddress(string $mail) : array + { + $addresses = []; + $list = \explode(',', $mail); + + foreach ($list as $address) { + $address = \trim($address); + + if (\stripos($address, '<') === false) { + if (($address = $this->normalizeEmailAddress($address)) !== null) { + $addresses[$address] = ''; + } + } else { + $parts = \explode('<', $address); + $address = \trim(\str_replace('>', '', $parts[1])); + + if (($address = $this->normalizeEmailAddress($address)) !== null) { + $addresses[$address] = \trim(\str_replace(['"', '\''], '', $parts[0])); + } + } + } + + return $addresses; + } + + /** + * Check if text has none ascii characters + * + * @param string $text Text to check + * + * @return bool + * + * @since 1.0.0 + */ + private function hasNoneASCII(string $text) : bool + { + return (bool) \preg_match('/[\x80-\xFF]/', $text); + } + + /** + * Define the message type based on the content + * + * @return void + * + * @since 1.0.0 + */ + private function setMessageType() : void + { + $this->messageType = ''; + + $type = []; + if (!empty($this->bodyAlt)) { + $type[] = DispositionType::ALT; + } + + foreach ($this->attachment as $attachment) { + if ($attachment['disposition'] === DispositionType::INLINE) { + $type[] = DispositionType::INLINE; + } elseif ($attachment['disposition'] === DispositionType::ATTACHMENT) { + $type[] = DispositionType::ATTACHMENT; + } + } + + $this->messageType = \implode('_', $type); + $this->messageType = empty($this->messageType) ? DispositionType::PLAIN : $this->messageType; + } + + /** + * Create the mail body + * + * @return string + * + * @since 1.0.0 + */ + public function createBody() : string + { + $this->id = empty($this->id) ? $this->generatedId() : $this->id; + + $output = ''; + $this->boundary[0] = 'b0_' . $this->id; + $this->boundary[1] = 'b1_' . $this->id; + $this->boundary[2] = 'b2_' . $this->id; + $this->boundary[3] = 'b3_' . $this->id; + + $output .= !empty($this->signKeyFile) ? $this->generateMimeHeader() . $this->endOfLine : ''; + + $body = $this->wrapText($this->body, $this->wordWrap, false); + + + return $output; + } + + /** + * Normalize text + * + * Line break + * + * @param string $text Text to normalized + * @param string $lb Line break + */ + private function normalizeText(string $text, string $lb = "\n") : string + { + return \str_replace(["\r\n", "\r", "\n"], $lb, $text); + } + + /** + * Generate a random id + * + * @return string + * + * @since 1.0.0 + */ + private function generatedId() : string + { + $rand = ''; + + try { + $rand = \random_bytes(32); + } catch (\Throwable $t) { + $rand = \hash('sha256', \uniqid((string) \mt_rand(), true), true); + } + + return \base64_encode(\hash('sha256', $rand, true)); + } + + /** + * Generate the mime header + * + * @return string + * + * @since 1.0.0 + */ + private function generateMimeHeader() : string + { + $mime = ''; + $isMultipart = true; + + switch ($this->messageType) { + case DispositionType::INLINE: + $mime .= 'Content-Type:' . MimeType::M_RELATED . ';' . $this->endOfLine;; + $mime .= ' boundary="' . $this->boundary[0] . '"' . $this->endOfLine; + break; + case DispositionType::ATTACHMENT: + case DispositionType::INLINE . '_' . DispositionType::ATTACHMENT: + case DispositionType::ALT . '_' . DispositionType::ATTACHMENT: + case DispositionType::ALT . '_' . DispositionType::INLINE . '_' . DispositionType::ATTACHMENT: + $mime .= 'Content-Type:' . MimeType::M_MIXED . ';' . $this->endOfLine;; + $mime .= ' boundary="' . $this->boundary[0] . '"' . $this->endOfLine; + break; + case DispositionType::ALT: + case DispositionType::ALT . '_' . DispositionType::INLINE: + $mime .= 'Content-Type:' . MimeType::M_ALT . ';' . $this->endOfLine;; + $mime .= ' boundary="' . $this->boundary[0] . '"' . $this->endOfLine; + break; + default: + $mime .= 'Content-Type:' . $this->contentType . '; charset=' . CharsetType::UTF_8 . ';' . $this->endOfLine;; + + $isMultipart = false; + } + + return $isMultipart && $this->encoding !== EncodingType::E_7BIT + ? 'Content-Transfer-Encoding:' . $this->encoding . ';' . $this->endOfLine + : $mime; + } + + private function wrapText(string $text, int $length, bool $quoted = false) : string + { + if ($length < 1) { + return $text; + } + + $softEndOfLine = $quoted ? ' =' . $this->endOfLine : $this->endOfLine; + + $text = $this->normalizeText($text, $this->endOfLine); + $text = \rtrim($text, "\r\n"); + + $lines = \explode($this->endOfLine, $text); + + $buffer = ''; + $output = ''; + foreach ($lines as $line) { + $words = \explode(' ', $line); + + foreach ($words as $word) { + if ($quoted && \strlen($word) > $length) { + + } else { + $oldBuf = $buffer; + $buffer .= $word . ' '; + + if (\strlen($buffer) > $length) { + $output .= \rtrim($oldBuf) . $softEndOfLine; + $buffer = $word; + } + } + } + + $output .= \rtrim($buffer) . $this->endOfLine; + } + + return $output; + } } diff --git a/Message/Mail/Pop3.php b/Message/Mail/Pop3.php index 2451a44d9..e30480466 100644 --- a/Message/Mail/Pop3.php +++ b/Message/Mail/Pop3.php @@ -40,4 +40,12 @@ class Pop3 extends EmailAbstract return parent::connect(); } + + /** + * {@inheritdoc} + */ + public function send() : bool + { + + } } diff --git a/Message/Mail/SubmitType.php b/Message/Mail/SubmitType.php new file mode 100644 index 000000000..345e14b02 --- /dev/null +++ b/Message/Mail/SubmitType.php @@ -0,0 +1,33 @@ +