1 | <?php |
||||||
2 | |||||||
3 | namespace Dacastro4\LaravelGmail\Services\Message; |
||||||
4 | |||||||
5 | use Carbon\Carbon; |
||||||
6 | use Dacastro4\LaravelGmail\GmailConnection; |
||||||
7 | use Dacastro4\LaravelGmail\Traits\HasDecodableBody; |
||||||
8 | use Dacastro4\LaravelGmail\Traits\HasParts; |
||||||
9 | use Dacastro4\LaravelGmail\Traits\Modifiable; |
||||||
10 | use Dacastro4\LaravelGmail\Traits\Replyable; |
||||||
11 | use Google_Service_Gmail; |
||||||
12 | use Google_Service_Gmail_MessagePart; |
||||||
13 | use Illuminate\Support\Collection; |
||||||
14 | use Illuminate\Support\Str; |
||||||
15 | |||||||
16 | /** |
||||||
17 | * Class SingleMessage |
||||||
18 | * |
||||||
19 | * @package Dacastro4\LaravelGmail\services |
||||||
20 | */ |
||||||
21 | class Mail extends GmailConnection |
||||||
22 | { |
||||||
23 | |||||||
24 | use HasDecodableBody, |
||||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
25 | Modifiable, |
||||||
26 | HasParts, |
||||||
27 | Replyable { |
||||||
28 | Replyable::__construct as private __rConstruct; |
||||||
29 | Modifiable::__construct as private __mConstruct; |
||||||
30 | } |
||||||
31 | |||||||
32 | /** |
||||||
33 | * @var |
||||||
34 | */ |
||||||
35 | public $id; |
||||||
36 | |||||||
37 | /** |
||||||
38 | * @var |
||||||
39 | */ |
||||||
40 | public $userId; |
||||||
41 | |||||||
42 | /** |
||||||
43 | * @var |
||||||
44 | */ |
||||||
45 | public $internalDate; |
||||||
46 | |||||||
47 | /** |
||||||
48 | * @var |
||||||
49 | */ |
||||||
50 | public $labels; |
||||||
51 | |||||||
52 | /** |
||||||
53 | * @var |
||||||
54 | */ |
||||||
55 | public $size; |
||||||
56 | |||||||
57 | /** |
||||||
58 | * @var |
||||||
59 | */ |
||||||
60 | public $threadId; |
||||||
61 | |||||||
62 | /** |
||||||
63 | * @var |
||||||
64 | */ |
||||||
65 | public $historyId; |
||||||
66 | |||||||
67 | /** |
||||||
68 | * @var \Google_Service_Gmail_MessagePart |
||||||
69 | */ |
||||||
70 | public $payload; |
||||||
71 | |||||||
72 | public $parts; |
||||||
73 | |||||||
74 | /** |
||||||
75 | * @var Google_Service_Gmail |
||||||
76 | */ |
||||||
77 | public $service; |
||||||
78 | |||||||
79 | /** |
||||||
80 | * SingleMessage constructor. |
||||||
81 | * |
||||||
82 | * @param \Google_Service_Gmail_Message $message |
||||||
83 | * @param bool $preload |
||||||
84 | * @param int $userId |
||||||
85 | */ |
||||||
86 | public function __construct(\Google_Service_Gmail_Message $message = null, $preload = false, $userId = null) |
||||||
87 | { |
||||||
88 | $this->service = new Google_Service_Gmail($this); |
||||||
89 | |||||||
90 | $this->__rConstruct(); |
||||||
91 | $this->__mConstruct(); |
||||||
92 | parent::__construct(config(), $userId); |
||||||
0 ignored issues
–
show
The call to
Dacastro4\LaravelGmail\T...difiable::__construct() has too many arguments starting with config() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
93 | |||||||
94 | if (!is_null($message)) { |
||||||
95 | if ($preload) { |
||||||
96 | $message = $this->service->users_messages->get('me', $message->getId()); |
||||||
97 | } |
||||||
98 | |||||||
99 | $this->setUserId($userId); |
||||||
100 | |||||||
101 | $this->setMessage($message); |
||||||
102 | |||||||
103 | if ($preload) { |
||||||
104 | $this->setMetadata(); |
||||||
105 | } |
||||||
106 | } |
||||||
107 | } |
||||||
108 | |||||||
109 | /** |
||||||
110 | * Set user Id |
||||||
111 | * |
||||||
112 | * @param int $userId |
||||||
113 | */ |
||||||
114 | protected function setUserId($userId) |
||||||
115 | { |
||||||
116 | $this->userId = $userId; |
||||||
117 | } |
||||||
118 | |||||||
119 | /** |
||||||
120 | * Sets data from mail |
||||||
121 | * |
||||||
122 | * @param \Google_Service_Gmail_Message $message |
||||||
123 | */ |
||||||
124 | protected function setMessage(\Google_Service_Gmail_Message $message) |
||||||
125 | { |
||||||
126 | $this->id = $message->getId(); |
||||||
127 | $this->internalDate = $message->getInternalDate(); |
||||||
128 | $this->labels = $message->getLabelIds(); |
||||||
129 | $this->size = $message->getSizeEstimate(); |
||||||
130 | $this->threadId = $message->getThreadId(); |
||||||
131 | $this->historyId = $message->getHistoryId(); |
||||||
132 | $this->payload = $message->getPayload(); |
||||||
133 | if ($this->payload) { |
||||||
134 | $this->parts = collect($this->payload->getParts()); |
||||||
135 | } |
||||||
136 | } |
||||||
137 | |||||||
138 | /** |
||||||
139 | * Sets the metadata from Mail when preloaded |
||||||
140 | */ |
||||||
141 | protected function setMetadata() |
||||||
142 | { |
||||||
143 | $this->to = $this->getTo(); |
||||||
144 | $from = $this->getFrom(); |
||||||
145 | $this->from = isset($from['email']) ? $from['email'] : null; |
||||||
146 | $this->nameFrom = isset($from['email']) ? $from['email'] : null; |
||||||
147 | |||||||
148 | $this->subject = $this->getSubject(); |
||||||
149 | } |
||||||
150 | |||||||
151 | /** |
||||||
152 | * Return a UNIX version of the date |
||||||
153 | * |
||||||
154 | * @return int UNIX date |
||||||
155 | */ |
||||||
156 | public function getInternalDate() |
||||||
157 | { |
||||||
158 | return $this->internalDate; |
||||||
159 | } |
||||||
160 | |||||||
161 | /** |
||||||
162 | * Returns the labels of the email |
||||||
163 | * Example: INBOX, STARRED, UNREAD |
||||||
164 | * |
||||||
165 | * @return array |
||||||
166 | */ |
||||||
167 | public function getLabels() |
||||||
168 | { |
||||||
169 | return $this->labels; |
||||||
170 | } |
||||||
171 | |||||||
172 | /** |
||||||
173 | * Returns approximate size of the email |
||||||
174 | * |
||||||
175 | * @return mixed |
||||||
176 | */ |
||||||
177 | public function getSize() |
||||||
178 | { |
||||||
179 | return $this->size; |
||||||
180 | } |
||||||
181 | |||||||
182 | /** |
||||||
183 | * Returns thread ID of the email |
||||||
184 | * |
||||||
185 | * @return string |
||||||
186 | */ |
||||||
187 | public function getThreadId() |
||||||
188 | { |
||||||
189 | return $this->threadId; |
||||||
190 | } |
||||||
191 | |||||||
192 | /** |
||||||
193 | * Returns history ID of the email |
||||||
194 | * |
||||||
195 | * @return string |
||||||
196 | */ |
||||||
197 | public function getHistoryId() |
||||||
198 | { |
||||||
199 | return $this->historyId; |
||||||
200 | } |
||||||
201 | |||||||
202 | /** |
||||||
203 | * Returns all the headers of the email |
||||||
204 | * |
||||||
205 | * @return Collection |
||||||
206 | */ |
||||||
207 | public function getHeaders() |
||||||
208 | { |
||||||
209 | return $this->buildHeaders($this->payload->getHeaders()); |
||||||
210 | } |
||||||
211 | |||||||
212 | /** |
||||||
213 | * Returns the subject of the email |
||||||
214 | * |
||||||
215 | * @return string |
||||||
216 | */ |
||||||
217 | public function getSubject() |
||||||
218 | { |
||||||
219 | return $this->getHeader('Subject'); |
||||||
220 | } |
||||||
221 | |||||||
222 | /** |
||||||
223 | * Returns the subject of the email |
||||||
224 | * |
||||||
225 | * @return array|string |
||||||
226 | */ |
||||||
227 | public function getReplyTo() |
||||||
228 | { |
||||||
229 | $replyTo = $this->getHeader('Reply-To'); |
||||||
230 | |||||||
231 | return $this->getFrom($replyTo ? $replyTo : $this->getHeader('From')); |
||||||
232 | } |
||||||
233 | |||||||
234 | /** |
||||||
235 | * Returns array of name and email of each recipient |
||||||
236 | * |
||||||
237 | * @param string|null $email |
||||||
238 | * @return array |
||||||
239 | */ |
||||||
240 | public function getFrom($email = null) |
||||||
241 | { |
||||||
242 | $from = $email ? $email : $this->getHeader('From'); |
||||||
243 | |||||||
244 | preg_match('/<(.*)>/', $from, $matches); |
||||||
0 ignored issues
–
show
It seems like
$from can also be of type null ; however, parameter $subject of preg_match() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
245 | |||||||
246 | $name = preg_replace('/ <(.*)>/', '', $from); |
||||||
247 | |||||||
248 | return [ |
||||||
249 | 'name' => $name, |
||||||
250 | 'email' => isset($matches[1]) ? $matches[1] : null, |
||||||
251 | ]; |
||||||
252 | } |
||||||
253 | |||||||
254 | /** |
||||||
255 | * Returns email of sender |
||||||
256 | * |
||||||
257 | * @return string|null |
||||||
258 | */ |
||||||
259 | public function getFromEmail() |
||||||
260 | { |
||||||
261 | $from = $this->getHeader('From'); |
||||||
262 | |||||||
263 | if (filter_var($from, FILTER_VALIDATE_EMAIL)) { |
||||||
264 | return $from; |
||||||
265 | } |
||||||
266 | |||||||
267 | preg_match('/<(.*)>/', $from, $matches); |
||||||
0 ignored issues
–
show
It seems like
$from can also be of type null ; however, parameter $subject of preg_match() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
268 | |||||||
269 | return isset($matches[1]) ? $matches[1] : null; |
||||||
270 | } |
||||||
271 | |||||||
272 | /** |
||||||
273 | * Returns name of the sender |
||||||
274 | * |
||||||
275 | * @return string|null |
||||||
276 | */ |
||||||
277 | public function getFromName() |
||||||
278 | { |
||||||
279 | $from = $this->getHeader('From'); |
||||||
280 | |||||||
281 | $name = preg_replace('/ <(.*)>/', '', $from); |
||||||
282 | |||||||
283 | return $name; |
||||||
284 | } |
||||||
285 | |||||||
286 | /** |
||||||
287 | * Returns array list of recipients |
||||||
288 | * |
||||||
289 | * @return array |
||||||
290 | */ |
||||||
291 | public function getTo() |
||||||
292 | { |
||||||
293 | $allTo = $this->getHeader('To'); |
||||||
294 | |||||||
295 | return $this->formatEmailList($allTo); |
||||||
296 | } |
||||||
297 | |||||||
298 | /** |
||||||
299 | * Returns array list of cc recipients |
||||||
300 | * |
||||||
301 | * @return array |
||||||
302 | */ |
||||||
303 | public function getCc() |
||||||
304 | { |
||||||
305 | $allCc = $this->getHeader('Cc'); |
||||||
306 | |||||||
307 | return $this->formatEmailList($allCc); |
||||||
308 | } |
||||||
309 | |||||||
310 | /** |
||||||
311 | * Returns array list of bcc recipients |
||||||
312 | * |
||||||
313 | * @return array |
||||||
314 | */ |
||||||
315 | public function getBcc() |
||||||
316 | { |
||||||
317 | $allBcc = $this->getHeader('Bcc'); |
||||||
318 | |||||||
319 | return $this->formatEmailList($allBcc); |
||||||
320 | } |
||||||
321 | |||||||
322 | /** |
||||||
323 | * Returns an array of emails from an string in RFC 822 format |
||||||
324 | * |
||||||
325 | * @param string $emails email list in RFC 822 format |
||||||
326 | * |
||||||
327 | * @return array |
||||||
328 | */ |
||||||
329 | public function formatEmailList($emails) |
||||||
330 | { |
||||||
331 | $all = []; |
||||||
332 | $explodedEmails = explode(',', $emails); |
||||||
333 | |||||||
334 | foreach ($explodedEmails as $email) { |
||||||
335 | |||||||
336 | $item = []; |
||||||
337 | |||||||
338 | preg_match('/<(.*)>/', $email, $matches); |
||||||
339 | |||||||
340 | $item['email'] = str_replace(' ', '', isset($matches[1]) ? $matches[1] : $email); |
||||||
341 | |||||||
342 | $name = preg_replace('/ <(.*)>/', '', $email); |
||||||
343 | |||||||
344 | if (Str::startsWith($name, ' ')) { |
||||||
345 | $name = substr($name, 1); |
||||||
346 | } |
||||||
347 | |||||||
348 | $item['name'] = str_replace("\"", '', $name ?: null); |
||||||
349 | |||||||
350 | $all[] = $item; |
||||||
351 | |||||||
352 | } |
||||||
353 | |||||||
354 | return $all; |
||||||
355 | } |
||||||
356 | |||||||
357 | /** |
||||||
358 | * Returns the original date that the email was sent |
||||||
359 | * |
||||||
360 | * @return Carbon |
||||||
361 | */ |
||||||
362 | public function getDate() |
||||||
363 | { |
||||||
364 | return Carbon::parse($this->getHeader('Date')); |
||||||
365 | } |
||||||
366 | |||||||
367 | /** |
||||||
368 | * Returns email of the original recipient |
||||||
369 | * |
||||||
370 | * @return string |
||||||
371 | */ |
||||||
372 | public function getDeliveredTo() |
||||||
373 | { |
||||||
374 | return $this->getHeader('Delivered-To'); |
||||||
375 | } |
||||||
376 | |||||||
377 | /** |
||||||
378 | * Base64 version of the body |
||||||
379 | * |
||||||
380 | * @return string |
||||||
381 | */ |
||||||
382 | public function getRawPlainTextBody() |
||||||
383 | { |
||||||
384 | return $this->getPlainTextBody(true); |
||||||
385 | } |
||||||
386 | |||||||
387 | /** |
||||||
388 | * @param bool $raw |
||||||
389 | * |
||||||
390 | * @return string |
||||||
391 | */ |
||||||
392 | public function getPlainTextBody($raw = false) |
||||||
393 | { |
||||||
394 | $content = $this->getBody(); |
||||||
395 | |||||||
396 | return $raw ? $content : $this->getDecodedBody($content); |
||||||
397 | } |
||||||
398 | |||||||
399 | /** |
||||||
400 | * Returns a specific body part from an email |
||||||
401 | * |
||||||
402 | * @param string $type |
||||||
403 | * |
||||||
404 | * @return null|string |
||||||
405 | * @throws \Exception |
||||||
406 | */ |
||||||
407 | public function getBody($type = 'text/plain') |
||||||
408 | { |
||||||
409 | $parts = $this->getAllParts($this->parts); |
||||||
410 | |||||||
411 | try { |
||||||
412 | if (!$parts->isEmpty()) { |
||||||
413 | foreach ($parts as $part) { |
||||||
414 | if ($part->mimeType == $type) { |
||||||
415 | return $part->body->data; |
||||||
416 | //if there are no parts in payload, try to get data from body->data |
||||||
417 | } elseif ($this->payload->body->data) { |
||||||
418 | return $this->payload->body->data; |
||||||
419 | } |
||||||
420 | } |
||||||
421 | } else { |
||||||
422 | return $this->payload->body->data; |
||||||
423 | } |
||||||
424 | } catch (\Exception $exception) { |
||||||
425 | throw new \Exception("Preload or load the single message before getting the body."); |
||||||
426 | } |
||||||
427 | |||||||
428 | return null; |
||||||
429 | } |
||||||
430 | |||||||
431 | /** |
||||||
432 | * True if message has at least one attachment. |
||||||
433 | * |
||||||
434 | * @return boolean |
||||||
435 | */ |
||||||
436 | public function hasAttachments() |
||||||
437 | { |
||||||
438 | $parts = $this->getAllParts($this->parts); |
||||||
439 | $has = false; |
||||||
440 | |||||||
441 | /** @var Google_Service_Gmail_MessagePart $part */ |
||||||
442 | foreach ($parts as $part) { |
||||||
443 | if (!empty($part->body->attachmentId) && $part->getFilename() != null && strlen($part->getFilename()) > 0) { |
||||||
444 | $has = true; |
||||||
445 | break; |
||||||
446 | } |
||||||
447 | } |
||||||
448 | |||||||
449 | return $has; |
||||||
450 | } |
||||||
451 | |||||||
452 | /** |
||||||
453 | * Number of attachments of the message. |
||||||
454 | * |
||||||
455 | * @return int |
||||||
456 | */ |
||||||
457 | public function countAttachments() |
||||||
458 | { |
||||||
459 | $numberOfAttachments = 0; |
||||||
460 | $parts = $this->getAllParts($this->parts); |
||||||
461 | |||||||
462 | foreach ($parts as $part) { |
||||||
463 | if (!empty($part->body->attachmentId)) { |
||||||
464 | $numberOfAttachments++; |
||||||
465 | } |
||||||
466 | } |
||||||
467 | |||||||
468 | return $numberOfAttachments; |
||||||
469 | } |
||||||
470 | |||||||
471 | /** |
||||||
472 | * Decodes the body from gmail to make it readable |
||||||
473 | * |
||||||
474 | * @param $content |
||||||
475 | * @return bool|string |
||||||
476 | */ |
||||||
477 | public function getDecodedBody($content) |
||||||
478 | { |
||||||
479 | $content = str_replace('_', '/', str_replace('-', '+', $content)); |
||||||
480 | |||||||
481 | return base64_decode($content); |
||||||
482 | } |
||||||
483 | |||||||
484 | /** |
||||||
485 | * @return string base64 version of the body |
||||||
486 | */ |
||||||
487 | public function getRawHtmlBody() |
||||||
488 | { |
||||||
489 | return $this->getHtmlBody(true); |
||||||
490 | } |
||||||
491 | |||||||
492 | /** |
||||||
493 | * Gets the HTML body |
||||||
494 | * |
||||||
495 | * @param bool $raw |
||||||
496 | * |
||||||
497 | * @return string |
||||||
498 | */ |
||||||
499 | public function getHtmlBody($raw = false) |
||||||
500 | { |
||||||
501 | $content = $this->getBody('text/html'); |
||||||
502 | |||||||
503 | return $raw ? $content : $this->getDecodedBody($content); |
||||||
504 | } |
||||||
505 | |||||||
506 | /** |
||||||
507 | * Get a collection of attachments with full information |
||||||
508 | * |
||||||
509 | * @return Collection |
||||||
510 | * @throws \Exception |
||||||
511 | */ |
||||||
512 | public function getAttachmentsWithData() |
||||||
513 | { |
||||||
514 | return $this->getAttachments(true); |
||||||
515 | } |
||||||
516 | |||||||
517 | /** |
||||||
518 | * Returns a collection of attachments |
||||||
519 | * |
||||||
520 | * @param bool $preload Preload only the attachment's 'data'. |
||||||
521 | * But does not load the other attachment info like filename, mimetype, etc.. |
||||||
522 | * |
||||||
523 | * @return Collection |
||||||
524 | * @throws \Exception |
||||||
525 | */ |
||||||
526 | public function getAttachments($preload = false) |
||||||
527 | { |
||||||
528 | $attachments = new Collection(); |
||||||
529 | $parts = $this->getAllParts($this->parts); |
||||||
530 | |||||||
531 | foreach ($parts as $part) { |
||||||
532 | if (!empty($part->body->attachmentId)) { |
||||||
533 | $attachment = (new Attachment($part->body->attachmentId, $part, $this->userId)); |
||||||
534 | |||||||
535 | if ($preload) { |
||||||
536 | $attachment = $attachment->getData(); |
||||||
537 | } |
||||||
538 | |||||||
539 | $attachments->push($attachment); |
||||||
540 | } |
||||||
541 | } |
||||||
542 | |||||||
543 | return $attachments; |
||||||
544 | } |
||||||
545 | |||||||
546 | /** |
||||||
547 | * Returns ID of the email |
||||||
548 | * |
||||||
549 | * @return string |
||||||
550 | */ |
||||||
551 | public function getId() |
||||||
552 | { |
||||||
553 | return $this->id; |
||||||
554 | } |
||||||
555 | |||||||
556 | /** |
||||||
557 | * Gets the user email from the config file |
||||||
558 | * |
||||||
559 | * @return mixed|null |
||||||
560 | */ |
||||||
561 | public function getUser() |
||||||
562 | { |
||||||
563 | return $this->config('email'); |
||||||
564 | } |
||||||
565 | |||||||
566 | /** |
||||||
567 | * Get's the gmail information from the Mail |
||||||
568 | * |
||||||
569 | * @return Mail |
||||||
570 | */ |
||||||
571 | public function load() |
||||||
572 | { |
||||||
573 | $message = $this->service->users_messages->get('me', $this->getId()); |
||||||
574 | |||||||
575 | return new self($message); |
||||||
576 | } |
||||||
577 | |||||||
578 | /** |
||||||
579 | * Sets the access token in case we wanna use a different token |
||||||
580 | * |
||||||
581 | * @param string $token |
||||||
582 | * |
||||||
583 | * @return Mail |
||||||
584 | */ |
||||||
585 | public function using($token) |
||||||
586 | { |
||||||
587 | $this->setToken($token); |
||||||
588 | |||||||
589 | return $this; |
||||||
590 | } |
||||||
591 | |||||||
592 | /** |
||||||
593 | * checks if message has at least one part without iterating through all parts |
||||||
594 | * |
||||||
595 | * @return bool |
||||||
596 | */ |
||||||
597 | public function hasParts() |
||||||
598 | { |
||||||
599 | return !!$this->iterateParts($this->parts, $returnOnFirstFound = true); |
||||||
600 | } |
||||||
601 | |||||||
602 | /** |
||||||
603 | * Gets all the headers from an email and returns a collections |
||||||
604 | * |
||||||
605 | * @param $emailHeaders |
||||||
606 | * @return Collection |
||||||
607 | */ |
||||||
608 | private function buildHeaders($emailHeaders) |
||||||
609 | { |
||||||
610 | $headers = []; |
||||||
611 | |||||||
612 | foreach ($emailHeaders as $header) { |
||||||
613 | /** @var \Google_Service_Gmail_MessagePartHeader $header */ |
||||||
614 | |||||||
615 | $head = new \stdClass(); |
||||||
616 | |||||||
617 | $head->key = $header->getName(); |
||||||
618 | $head->value = $header->getValue(); |
||||||
619 | |||||||
620 | $headers[] = $head; |
||||||
621 | } |
||||||
622 | |||||||
623 | return collect($headers); |
||||||
624 | } |
||||||
625 | } |
||||||
626 |