Completed
Push — master ( f60600...7db240 )
by Daniel
14s queued 11s
created

Mail::getDeliveredTo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
The trait Dacastro4\LaravelGmail\Traits\Replyable requires the property $users_messages which is not provided by Dacastro4\LaravelGmail\Services\Message\Mail.
Loading history...
Bug introduced by
The trait Dacastro4\LaravelGmail\Traits\Modifiable requires the property $users_messages which is not provided by Dacastro4\LaravelGmail\Services\Message\Mail.
Loading history...
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 \Google_Service_Gmail_MessagePart
64
	 */
65
	public $payload;
66
67
	public $parts;
68
69
	/**
70
	 * @var Google_Service_Gmail
71
	 */
72
	public $service;
73
74
	/**
75
	 * SingleMessage constructor.
76
	 *
77
	 * @param \Google_Service_Gmail_Message $message
78
	 * @param bool $preload
79
	 * @param  int 	$userId
80
	 */
81
	public function __construct(\Google_Service_Gmail_Message $message = null, $preload = false, $userId = null)
82
	{
83
		$this->service = new Google_Service_Gmail($this);
84
85
		$this->__rConstruct();
86
		$this->__mConstruct();
87
		parent::__construct(config(), $userId);
0 ignored issues
show
Unused Code introduced by
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 ignore-call  annotation

87
		parent::/** @scrutinizer ignore-call */ 
88
          __construct(config(), $userId);

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.

Loading history...
88
89
		if (!is_null($message)) {
90
			if ($preload) {
91
				$message = $this->service->users_messages->get('me', $message->getId());
92
			}
93
94
			$this->setUserId($userId);
95
96
			$this->setMessage($message);
97
98
			if ($preload) {
99
				$this->setMetadata();
100
			}
101
		}
102
	}
103
104
	/**
105
	 * Set user Id
106
	 *
107
	 * @param int $userId
108
	 */
109
	protected function setUserId($userId)
110
	{
111
		$this->userId = $userId;
112
	}
113
114
	/**
115
	 * Sets data from mail
116
	 *
117
	 * @param \Google_Service_Gmail_Message $message
118
	 */
119
	protected function setMessage(\Google_Service_Gmail_Message $message)
120
	{
121
		$this->id = $message->getId();
122
		$this->internalDate = $message->getInternalDate();
123
		$this->labels = $message->getLabelIds();
124
		$this->size = $message->getSizeEstimate();
125
		$this->threadId = $message->getThreadId();
126
		$this->payload = $message->getPayload();
127
		if ($this->payload) {
128
			$this->parts = collect($this->payload->getParts());
129
		}
130
	}
131
132
	/**
133
	 * Sets the metadata from Mail when preloaded
134
	 */
135
	protected function setMetadata()
136
	{
137
		$this->to = $this->getTo();
138
		$from = $this->getFrom();
139
		$this->from = isset($from['email']) ? $from['email'] : null;
140
		$this->nameFrom = isset($from['email']) ? $from['email'] : null;
141
142
		$this->subject = $this->getSubject();
143
	}
144
145
	/**
146
	 * Return a UNIX version of the date
147
	 *
148
	 * @return int UNIX date
149
	 */
150
	public function getInternalDate()
151
	{
152
		return $this->internalDate;
153
	}
154
155
	/**
156
	 * Returns the labels of the email
157
	 * Example: INBOX, STARRED, UNREAD
158
	 *
159
	 * @return array
160
	 */
161
	public function getLabels()
162
	{
163
		return $this->labels;
164
	}
165
166
	/**
167
	 * Returns approximate size of the email
168
	 *
169
	 * @return mixed
170
	 */
171
	public function getSize()
172
	{
173
		return $this->size;
174
	}
175
176
	/**
177
	 * Returns thread ID of the email
178
	 *
179
	 * @return string
180
	 */
181
	public function getThreadId()
182
	{
183
		return $this->threadId;
184
	}
185
186
	/**
187
	 * Returns all the headers of the email
188
	 *
189
	 * @return Collection
190
	 */
191
	public function getHeaders()
192
	{
193
		return $this->buildHeaders($this->payload->getHeaders());
194
	}
195
196
	/**
197
	 * Returns the subject of the email
198
	 *
199
	 * @return string
200
	 */
201
	public function getSubject()
202
	{
203
		return $this->getHeader('Subject');
204
	}
205
206
	/**
207
	 * Returns the subject of the email
208
	 *
209
	 * @return array|string
210
	 */
211
	public function getReplyTo()
212
	{
213
		$replyTo = $this->getHeader('Reply-To');
214
215
		return $this->getFrom($replyTo ? $replyTo : $this->getHeader('From'));
216
	}
217
218
	/**
219
	 * Returns array of name and email of each recipient
220
	 *
221
	 * @param string|null $email
222
	 * @return array
223
	 */
224
	public function getFrom($email = null)
225
	{
226
		$from = $email ? $email : $this->getHeader('From');
227
228
		preg_match('/<(.*)>/', $from, $matches);
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

228
		preg_match('/<(.*)>/', /** @scrutinizer ignore-type */ $from, $matches);
Loading history...
229
230
		$name = preg_replace('/ <(.*)>/', '', $from);
231
232
		return [
233
			'name'  => $name,
234
			'email' => isset($matches[1]) ? $matches[1] : null,
235
		];
236
	}
237
238
	/**
239
	 * Returns email of sender
240
	 *
241
	 * @return string|null
242
	 */
243
	public function getFromEmail()
244
	{
245
		$from = $this->getHeader('From');
246
247
		if (filter_var($from, FILTER_VALIDATE_EMAIL)) {
248
			return $from;
249
		}
250
251
		preg_match('/<(.*)>/', $from, $matches);
0 ignored issues
show
Bug introduced by
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 ignore-type  annotation

251
		preg_match('/<(.*)>/', /** @scrutinizer ignore-type */ $from, $matches);
Loading history...
252
253
		return isset($matches[1]) ? $matches[1] : null;
254
	}
255
256
	/**
257
	 * Returns name of the sender
258
	 *
259
	 * @return string|null
260
	 */
261
	public function getFromName()
262
	{
263
		$from = $this->getHeader('From');
264
265
		$name = preg_replace('/ <(.*)>/', '', $from);
266
267
		return $name;
268
	}
269
270
	/**
271
	 * Returns array list of recipients
272
	 *
273
	 * @return array
274
	 */
275
	public function getTo()
276
	{
277
		$allTo = $this->getHeader('To');
278
279
		return $this->formatEmailList($allTo);
280
	}
281
282
	/**
283
	 * Returns array list of cc recipients
284
	 *
285
	 * @return array
286
	 */
287
	public function getCc()
288
	{
289
		$allCc = $this->getHeader('Cc');
290
291
		return $this->formatEmailList($allCc);
292
	}
293
294
	/**
295
	 * Returns array list of bcc recipients
296
	 *
297
	 * @return array
298
	 */
299
	public function getBcc()
300
	{
301
		$allBcc = $this->getHeader('Bcc');
302
303
		return $this->formatEmailList($allBcc);
304
	}
305
306
	/**
307
	 * Returns an array of emails from an string in RFC 822 format
308
	 *
309
	 * @param string $emails email list in RFC 822 format
310
	 *
311
	 * @return array
312
	 */
313
	public function formatEmailList($emails)
314
	{
315
		$all = [];
316
		$explodedEmails = explode(',', $emails);
317
318
		foreach ($explodedEmails as $email) {
319
320
			$item = [];
321
322
			preg_match('/<(.*)>/', $email, $matches);
323
324
			$item['email'] = str_replace(' ', '', isset($matches[1]) ? $matches[1] : $email);
325
326
			$name = preg_replace('/ <(.*)>/', '', $email);
327
328
			if (Str::startsWith($name, ' ')) {
329
				$name = substr($name, 1);
330
			}
331
332
			$item['name'] = str_replace("\"", '', $name ?: null);
333
334
			$all[] = $item;
335
336
		}
337
338
		return $all;
339
	}
340
341
	/**
342
	 * Returns the original date that the email was sent
343
	 *
344
	 * @return Carbon
345
	 */
346
	public function getDate()
347
	{
348
		return Carbon::parse($this->getHeader('Date'));
349
	}
350
351
	/**
352
	 * Returns email of the original recipient
353
	 *
354
	 * @return string
355
	 */
356
	public function getDeliveredTo()
357
	{
358
		return $this->getHeader('Delivered-To');
359
	}
360
361
	/**
362
	 * Base64 version of the body
363
	 *
364
	 * @return string
365
	 */
366
	public function getRawPlainTextBody()
367
	{
368
		return $this->getPlainTextBody(true);
369
	}
370
371
	/**
372
	 * @param bool $raw
373
	 *
374
	 * @return string
375
	 */
376
	public function getPlainTextBody($raw = false)
377
	{
378
		$content = $this->getBody();
379
380
		return $raw ? $content : $this->getDecodedBody($content);
381
	}
382
383
	/**
384
	 * Returns a specific body part from an email
385
	 *
386
	 * @param string $type
387
	 *
388
	 * @return null|string
389
	 * @throws \Exception
390
	 */
391
	public function getBody($type = 'text/plain')
392
	{
393
		$parts = $this->getAllParts($this->parts);
394
395
		try {
396
			if (!$parts->isEmpty()) {
397
				foreach ($parts as $part) {
398
					if ($part->mimeType == $type) {
399
						return $part->body->data;
400
						//if there are no parts in payload, try to get data from body->data
401
					} elseif ($this->payload->body->data) {
402
						return $this->payload->body->data;
403
					}
404
				}
405
			} else {
406
				return $this->payload->body->data;
407
			}
408
		} catch (\Exception $exception) {
409
			throw new \Exception("Preload or load the single message before getting the body.");
410
		}
411
412
		return null;
413
	}
414
415
	/**
416
	 * True if message has at least one attachment.
417
	 *
418
	 * @return boolean
419
	 */
420
	public function hasAttachments()
421
	{
422
		$parts = $this->getAllParts($this->parts);
423
		$has = false;
424
425
		/** @var Google_Service_Gmail_MessagePart $part */
426
		foreach ($parts as $part) {
427
			if (!empty($part->body->attachmentId) && $part->getFilename() != null && strlen($part->getFilename()) > 0) {
428
				$has = true;
429
				break;
430
			}
431
		}
432
433
		return $has;
434
	}
435
436
	/**
437
	 * Number of attachments of the message.
438
	 *
439
	 * @return int
440
	 */
441
	public function countAttachments()
442
	{
443
		$numberOfAttachments = 0;
444
		$parts = $this->getAllParts($this->parts);
445
446
		foreach ($parts as $part) {
447
			if (!empty($part->body->attachmentId)) {
448
				$numberOfAttachments++;
449
			}
450
		}
451
452
		return $numberOfAttachments;
453
	}
454
455
	/**
456
	 * Decodes the body from gmail to make it readable
457
	 *
458
	 * @param $content
459
	 * @return bool|string
460
	 */
461
	public function getDecodedBody($content)
462
	{
463
		$content = str_replace('_', '/', str_replace('-', '+', $content));
464
465
		return base64_decode($content);
466
	}
467
468
	/**
469
	 * @return string base64 version of the body
470
	 */
471
	public function getRawHtmlBody()
472
	{
473
		return $this->getHtmlBody(true);
474
	}
475
476
	/**
477
	 * Gets the HTML body
478
	 *
479
	 * @param bool $raw
480
	 *
481
	 * @return string
482
	 */
483
	public function getHtmlBody($raw = false)
484
	{
485
		$content = $this->getBody('text/html');
486
487
		return $raw ? $content : $this->getDecodedBody($content);
488
	}
489
490
	/**
491
	 * Get a collection of attachments with full information
492
	 *
493
	 * @return Collection
494
	 * @throws \Exception
495
	 */
496
	public function getAttachmentsWithData()
497
	{
498
		return $this->getAttachments(true);
499
	}
500
501
	/**
502
	 * Returns a collection of attachments
503
	 *
504
	 * @param bool $preload Preload only the attachment's 'data'.
505
	 * But does not load the other attachment info like filename, mimetype, etc..
506
	 *
507
	 * @return Collection
508
	 * @throws \Exception
509
	 */
510
	public function getAttachments($preload = false)
511
	{
512
		$attachments = new Collection();
513
		$parts = $this->getAllParts($this->parts);
514
515
		foreach ($parts as $part) {
516
			if (!empty($part->body->attachmentId)) {
517
				$attachment = (new Attachment($part->body->attachmentId, $part, $this->userId));
518
519
				if ($preload) {
520
					$attachment = $attachment->getData();
521
				}
522
523
				$attachments->push($attachment);
524
			}
525
		}
526
527
		return $attachments;
528
	}
529
530
	/**
531
	 * Returns ID of the email
532
	 *
533
	 * @return string
534
	 */
535
	public function getId()
536
	{
537
		return $this->id;
538
	}
539
540
	/**
541
	 * Gets the user email from the config file
542
	 *
543
	 * @return mixed|null
544
	 */
545
	public function getUser()
546
	{
547
		return $this->config('email');
548
	}
549
550
	/**
551
	 * Get's the gmail information from the Mail
552
	 *
553
	 * @return Mail
554
	 */
555
	public function load()
556
	{
557
		$message = $this->service->users_messages->get('me', $this->getId());
558
559
		return new self($message);
560
	}
561
562
	/**
563
	 * Sets the access token in case we wanna use a different token
564
	 *
565
	 * @param string $token
566
	 *
567
	 * @return Mail
568
	 */
569
	public function using($token)
570
	{
571
		$this->setToken($token);
572
573
		return $this;
574
	}
575
576
	/**
577
	 * checks if message has at least one part without iterating through all parts
578
	 *
579
	 * @return bool
580
	 */
581
	public function hasParts()
582
	{
583
		return !!$this->iterateParts($this->parts, $returnOnFirstFound = true);
584
	}
585
586
	/**
587
	 * Gets all the headers from an email and returns a collections
588
	 *
589
	 * @param $emailHeaders
590
	 * @return Collection
591
	 */
592
	private function buildHeaders($emailHeaders)
593
	{
594
		$headers = [];
595
596
		foreach ($emailHeaders as $header) {
597
			/** @var \Google_Service_Gmail_MessagePartHeader $header */
598
599
			$head = new \stdClass();
600
601
			$head->key = $header->getName();
602
			$head->value = $header->getValue();
603
604
			$headers[] = $head;
605
		}
606
607
		return collect($headers);
608
	}
609
}
610