zbateson /
mail-mime-parser
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * This file is part of the ZBateson\MailMimeParser project. |
||
| 4 | * |
||
| 5 | * @license http://opensource.org/licenses/bsd-license.php BSD |
||
| 6 | */ |
||
| 7 | |||
| 8 | namespace ZBateson\MailMimeParser; |
||
| 9 | |||
| 10 | use GuzzleHttp\Psr7; |
||
| 11 | use Psr\Http\Message\StreamInterface; |
||
| 12 | use Psr\Log\LoggerInterface; |
||
| 13 | use ZBateson\MailMimeParser\Header\HeaderConsts; |
||
| 14 | use ZBateson\MailMimeParser\Message\Helper\MultipartHelper; |
||
| 15 | use ZBateson\MailMimeParser\Message\Helper\PrivacyHelper; |
||
| 16 | use ZBateson\MailMimeParser\Message\IMessagePart; |
||
| 17 | use ZBateson\MailMimeParser\Message\MimePart; |
||
| 18 | use ZBateson\MailMimeParser\Message\PartChildrenContainer; |
||
| 19 | use ZBateson\MailMimeParser\Message\PartFilter; |
||
| 20 | use ZBateson\MailMimeParser\Message\PartHeaderContainer; |
||
| 21 | use ZBateson\MailMimeParser\Message\PartStreamContainer; |
||
| 22 | |||
| 23 | /** |
||
| 24 | * An email message. |
||
| 25 | * |
||
| 26 | * The message could represent a simple text email, a multipart message with |
||
| 27 | * children, or a non-mime message containing UUEncoded parts. |
||
| 28 | * |
||
| 29 | * @author Zaahid Bateson |
||
| 30 | */ |
||
| 31 | class Message extends MimePart implements IMessage |
||
| 32 | { |
||
| 33 | /** |
||
| 34 | * @var MultipartHelper service providing functions for multipart messages. |
||
| 35 | */ |
||
| 36 | private MultipartHelper $multipartHelper; |
||
| 37 | |||
| 38 | /** |
||
| 39 | * @var PrivacyHelper service providing functions for multipart/signed |
||
| 40 | * messages. |
||
| 41 | */ |
||
| 42 | private PrivacyHelper $privacyHelper; |
||
| 43 | |||
| 44 | 119 | public function __construct( |
|
| 45 | ?LoggerInterface $logger = null, |
||
| 46 | ?PartStreamContainer $streamContainer = null, |
||
| 47 | ?PartHeaderContainer $headerContainer = null, |
||
| 48 | ?PartChildrenContainer $partChildrenContainer = null, |
||
| 49 | ?MultipartHelper $multipartHelper = null, |
||
| 50 | ?PrivacyHelper $privacyHelper = null |
||
| 51 | ) { |
||
| 52 | 119 | parent::__construct( |
|
| 53 | 119 | null, |
|
| 54 | 119 | $logger, |
|
| 55 | 119 | $streamContainer, |
|
| 56 | 119 | $headerContainer, |
|
| 57 | 119 | $partChildrenContainer |
|
| 58 | 119 | ); |
|
| 59 | 119 | $di = MailMimeParser::getGlobalContainer(); |
|
| 60 | 119 | $this->multipartHelper = $multipartHelper ?? $di->get(MultipartHelper::class); |
|
| 61 | 119 | $this->privacyHelper = $privacyHelper ?? $di->get(PrivacyHelper::class); |
|
| 62 | } |
||
| 63 | |||
| 64 | /** |
||
| 65 | * Convenience method to parse a handle or string into an IMessage without |
||
| 66 | * requiring including MailMimeParser, instantiating it, and calling parse. |
||
| 67 | * |
||
| 68 | * If the passed $resource is a resource handle or StreamInterface, the |
||
| 69 | * resource must remain open while the returned IMessage object exists. |
||
| 70 | * Pass true as the second argument to have the resource attached to the |
||
| 71 | * IMessage and closed for you when it's destroyed, or pass false to |
||
| 72 | * manually close it if it should remain open after the IMessage object is |
||
| 73 | * destroyed. |
||
| 74 | * |
||
| 75 | * @param resource|StreamInterface|string $resource The resource handle to |
||
| 76 | * the input stream of the mime message, or a string containing a |
||
| 77 | * mime message. |
||
| 78 | * @param bool $attached pass true to have it attached to the returned |
||
| 79 | * IMessage and destroyed with it. |
||
| 80 | */ |
||
| 81 | 1 | public static function from(mixed $resource, bool $attached) : IMessage |
|
| 82 | { |
||
| 83 | 1 | static $mmp = null; |
|
| 84 | 1 | if ($mmp === null) { |
|
| 85 | 1 | $mmp = new MailMimeParser(); |
|
| 86 | } |
||
| 87 | 1 | return $mmp->parse($resource, $attached); |
|
| 88 | } |
||
| 89 | |||
| 90 | /** |
||
| 91 | * Returns true if the current part is a mime part. |
||
| 92 | * |
||
| 93 | * The message is considered 'mime' if it has either a Content-Type or |
||
| 94 | * MIME-Version header defined. |
||
| 95 | * |
||
| 96 | */ |
||
| 97 | 20 | public function isMime() : bool |
|
| 98 | { |
||
| 99 | 20 | $contentType = $this->getHeaderValue(HeaderConsts::CONTENT_TYPE); |
|
| 100 | 20 | $mimeVersion = $this->getHeaderValue(HeaderConsts::MIME_VERSION); |
|
| 101 | 20 | return ($contentType !== null || $mimeVersion !== null); |
|
| 102 | } |
||
| 103 | |||
| 104 | 95 | public function getSubject() : ?string |
|
| 105 | { |
||
| 106 | 95 | return $this->getHeaderValue(HeaderConsts::SUBJECT); |
|
| 107 | } |
||
| 108 | |||
| 109 | 74 | public function getTextPart(int $index = 0) : ?IMessagePart |
|
| 110 | { |
||
| 111 | 74 | return $this->getPart( |
|
| 112 | 74 | $index, |
|
| 113 | 74 | PartFilter::fromInlineContentType('text/plain') |
|
| 114 | 74 | ); |
|
| 115 | } |
||
| 116 | |||
| 117 | 1 | public function getTextPartCount() : int |
|
| 118 | { |
||
| 119 | 1 | return $this->getPartCount( |
|
| 120 | 1 | PartFilter::fromInlineContentType('text/plain') |
|
| 121 | 1 | ); |
|
| 122 | } |
||
| 123 | |||
| 124 | 36 | public function getHtmlPart(int $index = 0) : ?IMessagePart |
|
| 125 | { |
||
| 126 | 36 | return $this->getPart( |
|
| 127 | 36 | $index, |
|
| 128 | 36 | PartFilter::fromInlineContentType('text/html') |
|
| 129 | 36 | ); |
|
| 130 | } |
||
| 131 | |||
| 132 | 1 | public function getHtmlPartCount() : int |
|
| 133 | { |
||
| 134 | 1 | return $this->getPartCount( |
|
| 135 | 1 | PartFilter::fromInlineContentType('text/html') |
|
| 136 | 1 | ); |
|
| 137 | } |
||
| 138 | |||
| 139 | 70 | public function getTextStream(int $index = 0, string $charset = MailMimeParser::DEFAULT_CHARSET) : ?StreamInterface |
|
| 140 | { |
||
| 141 | 70 | $textPart = $this->getTextPart($index); |
|
| 142 | 70 | if ($textPart !== null) { |
|
| 143 | 70 | return $textPart->getContentStream($charset); |
|
| 144 | } |
||
| 145 | 1 | return null; |
|
| 146 | } |
||
| 147 | |||
| 148 | 2 | public function getTextContent(int $index = 0, string $charset = MailMimeParser::DEFAULT_CHARSET) : ?string |
|
| 149 | { |
||
| 150 | 2 | $part = $this->getTextPart($index); |
|
| 151 | 2 | if ($part !== null) { |
|
| 152 | 2 | return $part->getContent($charset); |
|
| 153 | } |
||
| 154 | 1 | return null; |
|
| 155 | } |
||
| 156 | |||
| 157 | 30 | public function getHtmlStream(int $index = 0, string $charset = MailMimeParser::DEFAULT_CHARSET) : ?StreamInterface |
|
| 158 | { |
||
| 159 | 30 | $htmlPart = $this->getHtmlPart($index); |
|
| 160 | 30 | if ($htmlPart !== null) { |
|
| 161 | 30 | return $htmlPart->getContentStream($charset); |
|
| 162 | } |
||
| 163 | 1 | return null; |
|
| 164 | } |
||
| 165 | |||
| 166 | 2 | public function getHtmlContent(int $index = 0, string $charset = MailMimeParser::DEFAULT_CHARSET) : ?string |
|
| 167 | { |
||
| 168 | 2 | $part = $this->getHtmlPart($index); |
|
| 169 | 2 | if ($part !== null) { |
|
| 170 | 2 | return $part->getContent($charset); |
|
| 171 | } |
||
| 172 | 1 | return null; |
|
| 173 | } |
||
| 174 | |||
| 175 | 2 | public function setTextPart(mixed $resource, string $charset = 'UTF-8') : static |
|
| 176 | { |
||
| 177 | 2 | $this->multipartHelper |
|
| 178 | 2 | ->setContentPartForMimeType( |
|
| 179 | 2 | $this, |
|
| 180 | 2 | 'text/plain', |
|
| 181 | 2 | $resource, |
|
| 182 | 2 | $charset |
|
| 183 | 2 | ); |
|
| 184 | 2 | return $this; |
|
| 185 | } |
||
| 186 | |||
| 187 | 5 | public function setHtmlPart(mixed $resource, string $charset = 'UTF-8') : static |
|
| 188 | { |
||
| 189 | 5 | $this->multipartHelper |
|
| 190 | 5 | ->setContentPartForMimeType( |
|
| 191 | 5 | $this, |
|
| 192 | 5 | 'text/html', |
|
| 193 | 5 | $resource, |
|
| 194 | 5 | $charset |
|
| 195 | 5 | ); |
|
| 196 | 5 | return $this; |
|
| 197 | } |
||
| 198 | |||
| 199 | 4 | public function removeTextPart(int $index = 0) : bool |
|
| 200 | { |
||
| 201 | 4 | return $this->multipartHelper |
|
| 202 | 4 | ->removePartByMimeType( |
|
| 203 | 4 | $this, |
|
| 204 | 4 | 'text/plain', |
|
| 205 | 4 | $index |
|
| 206 | 4 | ); |
|
| 207 | } |
||
| 208 | |||
| 209 | 1 | public function removeAllTextParts(bool $moveRelatedPartsBelowMessage = true) : bool |
|
| 210 | { |
||
| 211 | 1 | return $this->multipartHelper |
|
| 212 | 1 | ->removeAllContentPartsByMimeType( |
|
| 213 | 1 | $this, |
|
| 214 | 1 | 'text/plain', |
|
| 215 | 1 | $moveRelatedPartsBelowMessage |
|
| 216 | 1 | ); |
|
| 217 | } |
||
| 218 | |||
| 219 | 3 | public function removeHtmlPart(int $index = 0) : bool |
|
| 220 | { |
||
| 221 | 3 | return $this->multipartHelper |
|
| 222 | 3 | ->removePartByMimeType( |
|
| 223 | 3 | $this, |
|
| 224 | 3 | 'text/html', |
|
| 225 | 3 | $index |
|
| 226 | 3 | ); |
|
| 227 | } |
||
| 228 | |||
| 229 | 2 | public function removeAllHtmlParts(bool $moveRelatedPartsBelowMessage = true) : bool |
|
| 230 | { |
||
| 231 | 2 | return $this->multipartHelper |
|
| 232 | 2 | ->removeAllContentPartsByMimeType( |
|
| 233 | 2 | $this, |
|
| 234 | 2 | 'text/html', |
|
| 235 | 2 | $moveRelatedPartsBelowMessage |
|
| 236 | 2 | ); |
|
| 237 | } |
||
| 238 | |||
| 239 | 5 | public function getAttachmentPart(int $index) : ?IMessagePart |
|
| 240 | { |
||
| 241 | 5 | return $this->getPart( |
|
| 242 | 5 | $index, |
|
| 243 | 5 | PartFilter::fromAttachmentFilter() |
|
| 244 | 5 | ); |
|
| 245 | } |
||
| 246 | |||
| 247 | 57 | public function getAllAttachmentParts() : array |
|
| 248 | { |
||
| 249 | 57 | return $this->getAllParts( |
|
| 250 | 57 | PartFilter::fromAttachmentFilter() |
|
| 251 | 57 | ); |
|
| 252 | } |
||
| 253 | |||
| 254 | 57 | public function getAttachmentCount() : int |
|
| 255 | { |
||
| 256 | 57 | return \count($this->getAllAttachmentParts()); |
|
| 257 | } |
||
| 258 | |||
| 259 | 5 | public function addAttachmentPart(mixed $resource, string $mimeType, ?string $filename = null, string $disposition = 'attachment', string $encoding = 'base64') : static |
|
| 260 | { |
||
| 261 | 5 | $this->multipartHelper |
|
| 262 | 5 | ->createAndAddPartForAttachment( |
|
| 263 | 5 | $this, |
|
| 264 | 5 | $resource, |
|
| 265 | 5 | $mimeType, |
|
| 266 | 5 | (\strcasecmp($disposition, 'inline') === 0) ? 'inline' : 'attachment', |
|
| 267 | 5 | $filename, |
|
| 268 | 5 | $encoding |
|
| 269 | 5 | ); |
|
| 270 | 5 | return $this; |
|
| 271 | } |
||
| 272 | |||
| 273 | 5 | public function addAttachmentPartFromFile(string $filePath, string $mimeType, ?string $filename = null, string $disposition = 'attachment', string $encoding = 'base64') : static |
|
| 274 | { |
||
| 275 | 5 | $handle = Psr7\Utils::streamFor(\fopen($filePath, 'r')); |
|
| 276 | 5 | if ($filename === null) { |
|
| 277 | 4 | $filename = \basename($filePath); |
|
| 278 | } |
||
| 279 | 5 | $this->addAttachmentPart($handle, $mimeType, $filename, $disposition, $encoding); |
|
| 280 | 5 | return $this; |
|
| 281 | } |
||
| 282 | |||
| 283 | 3 | public function removeAttachmentPart(int $index) : static |
|
| 284 | { |
||
| 285 | 3 | $part = $this->getAttachmentPart($index); |
|
| 286 | 3 | $this->removePart($part); |
|
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 287 | 3 | return $this; |
|
| 288 | } |
||
| 289 | |||
| 290 | 1 | public function getSignedMessageStream() : ?StreamInterface |
|
| 291 | { |
||
| 292 | 1 | return $this |
|
| 293 | 1 | ->privacyHelper |
|
| 294 | 1 | ->getSignedMessageStream($this); |
|
| 295 | } |
||
| 296 | |||
| 297 | 15 | public function getSignedMessageAsString() : ?string |
|
| 298 | { |
||
| 299 | 15 | return $this |
|
| 300 | 15 | ->privacyHelper |
|
| 301 | 15 | ->getSignedMessageAsString($this); |
|
| 302 | } |
||
| 303 | |||
| 304 | 62 | public function getSignaturePart() : ?IMessagePart |
|
| 305 | { |
||
| 306 | 62 | if (\strcasecmp($this->getContentType(), 'multipart/signed') === 0) { |
|
| 307 | 19 | return $this->getChild(1); |
|
| 308 | } |
||
| 309 | 43 | return null; |
|
| 310 | } |
||
| 311 | |||
| 312 | 10 | public function setAsMultipartSigned(string $micalg, string $protocol) : static |
|
| 313 | { |
||
| 314 | 10 | $this->privacyHelper |
|
| 315 | 10 | ->setMessageAsMultipartSigned($this, $micalg, $protocol); |
|
| 316 | 10 | return $this; |
|
| 317 | } |
||
| 318 | |||
| 319 | 10 | public function setSignature(string $body) : static |
|
| 320 | { |
||
| 321 | 10 | $this->privacyHelper |
|
| 322 | 10 | ->setSignature($this, $body); |
|
| 323 | 10 | return $this; |
|
| 324 | } |
||
| 325 | |||
| 326 | 73 | public function getMessageId() : ?string |
|
| 327 | { |
||
| 328 | 73 | return $this->getHeaderValue(HeaderConsts::MESSAGE_ID); |
|
| 329 | } |
||
| 330 | |||
| 331 | 73 | public function getErrorLoggingContextName() : string |
|
| 332 | { |
||
| 333 | 73 | $params = ''; |
|
| 334 | 73 | if (!empty($this->getMessageId())) { |
|
| 335 | 73 | $params .= ', message-id=' . $this->getContentId(); |
|
| 336 | } |
||
| 337 | 73 | $params .= ', content-type=' . $this->getContentType(); |
|
| 338 | 73 | $nsClass = static::class; |
|
| 339 | 73 | $class = \substr($nsClass, (\strrpos($nsClass, '\\') ?? -1) + 1); |
|
| 340 | 73 | return $class . '(' . \spl_object_id($this) . $params . ')'; |
|
| 341 | } |
||
| 342 | } |
||
| 343 |