Completed
Pull Request — master (#1523)
by
unknown
06:29
created

Account::deleteMailbox()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.0625

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 6
cts 8
cp 0.75
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
crap 2.0625
1
<?php
2
/**
3
 * @author Alexander Weidinger <[email protected]>
4
 * @author Christian Nöding <[email protected]>
5
 * @author Christoph Wurst <[email protected]>
6
 * @author Christoph Wurst <[email protected]>
7
 * @author Christoph Wurst <[email protected]>
8
 * @author Clement Wong <[email protected]>
9
 * @author Lukas Reschke <[email protected]>
10
 * @author Robin McCorkell <[email protected]>
11
 * @author Scrutinizer Auto-Fixer <[email protected]>
12
 * @author Thomas Imbreckx <[email protected]>
13
 * @author Thomas I <[email protected]>
14
 * @author Thomas Mueller <[email protected]>
15
 * @author Thomas Müller <[email protected]>
16
 *
17
 * ownCloud - Mail
18
 *
19
 * This code is free software: you can redistribute it and/or modify
20
 * it under the terms of the GNU Affero General Public License, version 3,
21
 * as published by the Free Software Foundation.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
 * GNU Affero General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU Affero General Public License, version 3,
29
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
30
 *
31
 */
32
33
namespace OCA\Mail;
34
35
use Horde_Imap_Client_Ids;
36
use Horde_Imap_Client_Mailbox;
37
use Horde_Imap_Client_Socket;
38
use Horde_Imap_Client;
39
use Horde_Mail_Rfc822_Address;
40
use Horde_Mail_Transport;
41
use Horde_Mail_Transport_Mail;
42
use Horde_Mail_Transport_Smtphorde;
43
use Horde_Mime_Headers_Date;
44
use Horde_Mime_Mail;
45
use Horde_Mime_Part;
46
use OCA\Mail\Cache\Cache;
47
use OCA\Mail\Db\MailAccount;
48
use OCA\Mail\Model\IMessage;
49
use OCA\Mail\Model\Message;
50
use OCA\Mail\Model\ReplyMessage;
51
use OCA\Mail\Service\AutoCompletion\AddressCollector;
52
use OCA\Mail\Service\IAccount;
53
use OCA\Mail\Service\IMailBox;
54
use OCP\IConfig;
55
use OCP\ICacheFactory;
56
use OCP\Security\ICrypto;
57
use OCA\Mail\Db\Alias;
58
59
class Account implements IAccount {
60
61
	/** @var MailAccount */
62
	private $account;
63
64
	/** @var Mailbox[]|null */
65
	private $mailboxes;
66
67
	/** @var Horde_Imap_Client_Socket */
68
	private $client;
69
70
	/** @var ICrypto */
71
	private $crypto;
72
73
	/** @var IConfig */
74
	private $config;
75
76
	/** @var ICacheFactory */
77
	private $memcacheFactory;
78
79
	/** @var Alias */
80
	private $alias;
81
82
	/**
83
	 * @param MailAccount $account
84
	 */
85 3
	public function __construct(MailAccount $account) {
86 3
		$this->account = $account;
87 3
		$this->mailboxes = null;
88 3
		$this->crypto = \OC::$server->getCrypto();
89 3
		$this->config = \OC::$server->getConfig();
90 3
		$this->memcacheFactory = \OC::$server->getMemcacheFactory();
91 3
		$this->alias = null;
92 3
	}
93
94
	/**
95
	 * @return int
96
	 */
97 6
	public function getId() {
98 6
		return $this->account->getId();
99
	}
100
101
	/**
102
	 * @param Alias $alias
103
	 * @return void
104
	 */
105
	public function setAlias($alias) {
106
		$this->alias = new Alias();
107
		$this->alias = $alias;
108
	}
109
110
	/**
111
	 * @return string
112
	 */
113
	public function getName() {
114
		return $this->alias ? $this->alias->getName() : $this->account->getName();
115
	}
116
117
	/**
118
	 * @return string
119
	 */
120 3
	public function getEMailAddress() {
121 3
		return $this->account->getEmail();
122
	}
123
124
	/**
125
	 * @return Horde_Imap_Client_Socket
126
	 */
127 13
	public function getImapConnection() {
128 13
		if (is_null($this->client)) {
129
			$host = $this->account->getInboundHost();
130
			$user = $this->account->getInboundUser();
131
			$password = $this->account->getInboundPassword();
132
			$password = $this->crypto->decrypt($password);
133
			$port = $this->account->getInboundPort();
134
			$ssl_mode = $this->convertSslMode($this->account->getInboundSslMode());
135
136
			$params = [
137
				'username' => $user,
138
				'password' => $password,
139
				'hostspec' => $host,
140
				'port' => $port,
141
				'secure' => $ssl_mode,
142
				'timeout' => 20,
143
			];
144 View Code Duplication
			if ($this->config->getSystemValue('app.mail.imaplog.enabled', false)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
145
				$params['debug'] = $this->config->getSystemValue('datadirectory') . '/horde_imap.log';
146
			}
147
			if ($this->config->getSystemValue('app.mail.server-side-cache.enabled', false)) {
148
				if ($this->memcacheFactory->isAvailable()) {
149
					$params['cache'] = [
150
						'backend' => new Cache(array(
151
							'cacheob' => $this->memcacheFactory
152
								->create(md5($this->getId() . $this->getEMailAddress()))
153
						))];
154
				}
155
			}
156
			$this->client = new \Horde_Imap_Client_Socket($params);
157
			$this->client->login();
158
		}
159 13
		return $this->client;
160
	}
161
162
	/**
163
	 * @param string $mailBox
164
	 * @return Mailbox
165
	 */
166 12
	public function createMailbox($mailBox) {
167 12
		$conn = $this->getImapConnection();
168 12
		$conn->createMailbox($mailBox);
169 12
		$this->mailboxes = null;
170
171 12
		return $this->getMailbox($mailBox);
172
	}
173
174
	/**
175
	 * Send a new message or reply to an existing message
176
	 *
177
	 * @param IMessage $message
178
	 * @param int|null $draftUID
179
	 */
180
	public function sendMessage(IMessage $message, $draftUID) {
181
		// build mime body
182
		$from = new Horde_Mail_Rfc822_Address($message->getFrom());
183
		$from->personal = $this->getName();
184
		$headers = [
185
			'From' => $from,
186
			'To' => $message->getToList(),
187
			'Cc' => $message->getCCList(),
188
			'Bcc' => $message->getBCCList(),
189
			'Subject' => $message->getSubject(),
190
		];
191
192
		if (!is_null($message->getRepliedMessage())) {
193
			$headers['In-Reply-To'] = $message->getRepliedMessage()->getMessageId();
194
		}
195
196
		$mail = new Horde_Mime_Mail();
197
		$mail->addHeaders($headers);
198
		$mail->setBody($message->getContent());
199
200
		// Append attachments
201
		foreach ($message->getAttachments() as $attachment) {
202
			$mail->addMimePart($attachment);
203
		}
204
205
		// Send the message
206
		$transport = $this->createTransport();
207
		$mail->send($transport, false, false);
208
209
		// Save the message in the sent folder
210
		$sentFolder = $this->getSentFolder();
211
		/** @var resource $raw */
212
		$raw = stream_get_contents($mail->getRaw());
213
		$sentFolder->saveMessage($raw, [
0 ignored issues
show
Documentation introduced by
$raw is of type resource, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
214
			Horde_Imap_Client::FLAG_SEEN
215
		]);
216
217
		// Delete draft if one exists
218
		if (!is_null($draftUID)) {
219
			$draftsFolder = $this->getDraftsFolder();
220
			$draftsFolder->setMessageFlag($draftUID, Horde_Imap_Client::FLAG_DELETED, true);
221
			$this->deleteDraft($draftUID);
222
		}
223
	}
224
225
	/**
226
	 * @param IMessage $message
227
	 * @param int|null $previousUID
228
	 * @return int
229
	 */
230
	public function saveDraft(IMessage $message, $previousUID) {
231
		// build mime body
232
		$from = new Horde_Mail_Rfc822_Address($message->getFrom());
233
		$from->personal = $this->getName();
234
		$headers = [
235
			'From' => $from,
236
			'To' => $message->getToList(),
237
			'Cc' => $message->getCCList(),
238
			'Bcc' => $message->getBCCList(),
239
			'Subject' => $message->getSubject(),
240
			'Date' => Horde_Mime_Headers_Date::create(),
241
		];
242
243
		$mail = new Horde_Mime_Mail();
244
		$mail->addHeaders($headers);
245
		$body = new Horde_Mime_Part();
246
		$body->setType('text/plain');
247
		$body->setContents($message->getContent());
248
		$mail->setBasePart($body);
249
250
		// create transport and save message
251
		// save the message in the drafts folder
252
		$draftsFolder = $this->getDraftsFolder();
253
		/** @var resource $raw */
254
		$raw = $mail->getRaw();
255
		$newUid = $draftsFolder->saveDraft(stream_get_contents($raw));
256
257
		// delete old version if one exists
258
		if (!is_null($previousUID)) {
259
			$draftsFolder->setMessageFlag($previousUID, \Horde_Imap_Client::FLAG_DELETED,
260
				true);
261
			$this->deleteDraft($previousUID);
262
		}
263
264
		return $newUid;
265
	}
266
267
	/**
268
	 * @param string $mailBox
269
	 */
270 12
	public function deleteMailbox($mailBox) {
271 12
		if ($mailBox instanceof Mailbox) {
272
			$mailBox = $mailBox->getFolderId();
273
		}
274 12
		$conn = $this->getImapConnection();
275 12
		$conn->deleteMailbox($mailBox);
276 3
		$this->mailboxes = null;
277 3
	}
278
279
	/**
280
	 * Lists mailboxes (folders) for this account.
281
	 *
282
	 * Lists mailboxes and also queries the server for their 'special use',
283
	 * eg. inbox, sent, trash, etc
284
	 *
285
	 * @param string $pattern Pattern to match mailboxes against. All by default.
286
	 * @return Mailbox[]
287
	 */
288 6
	protected function listMailboxes($pattern = '*') {
289
		// open the imap connection
290 6
		$conn = $this->getImapConnection();
291
292
		// if successful -> get all folders of that account
293 6
		$mailBoxes = $conn->listMailboxes($pattern, Horde_Imap_Client::MBOX_ALL,
294
			[
295 6
			'delimiter' => true,
296 6
			'attributes' => true,
297 6
			'special_use' => true,
298
			'sort' => true
299 6
		]);
300
301 6
		$mailboxes = [];
302 6
		foreach ($mailBoxes as $mailbox) {
303 6
			$mailboxes[] = new Mailbox($conn, $mailbox['mailbox'],
304 6
				$mailbox['attributes'], $mailbox['delimiter']);
305 6
			if ($mailbox['mailbox']->utf8 === 'INBOX') {
306 6
				$mailboxes[] = new SearchMailbox($conn, $mailbox['mailbox'],
307 6
					$mailbox['attributes'], $mailbox['delimiter']);
308 6
			}
309 6
		}
310
311 6
		return $mailboxes;
312
	}
313
314
	/**
315
	 * @param string $folderId
316
	 * @return \OCA\Mail\Mailbox
317
	 */
318 12
	public function getMailbox($folderId) {
319 12
		$conn = $this->getImapConnection();
320 12
		$parts = explode('/', $folderId);
321 12
		if (count($parts) > 1 && $parts[1] === 'FLAGGED') {
322
			$mailbox = new Horde_Imap_Client_Mailbox($parts[0]);
323
			return new SearchMailbox($conn, $mailbox, []);
324
		}
325 12
		$mailbox = new Horde_Imap_Client_Mailbox($folderId);
326 12
		return new Mailbox($conn, $mailbox, []);
327
	}
328
329
	/**
330
	 * Get a list of all mailboxes in this account
331
	 *
332
	 * @return Mailbox[]
333
	 */
334 7
	public function getMailboxes() {
335 7
		if ($this->mailboxes === null) {
336 6
			$this->mailboxes = $this->listMailboxes();
337 6
			$this->sortMailboxes();
338 6
			$this->localizeSpecialMailboxes();
339 6
		}
340
341 7
		return $this->mailboxes;
342
	}
343
344
	/**
345
	 * @return array
346
	 */
347 3
	public function getListArray() {
348
349 3
		$folders = [];
350 3
		$mailBoxes = $this->getMailboxes();
351
		$mailBoxNames = array_map(function($mb) {
352
			/** @var Mailbox $mb */
353 3
			return $mb->getFolderId();
354
		}, array_filter($mailBoxes, function($mb) {
355
			/** @var Mailbox $mb */
356 3
			return (!$mb instanceof SearchMailbox) && (!in_array('\noselect', $mb->attributes()));
357 3
		}));
358
359 3
		$status = $this->getImapConnection()->status($mailBoxNames);
360 3
		foreach ($mailBoxes as $mailbox) {
361 3
			$s = isset($status[$mailbox->getFolderId()]) ? $status[$mailbox->getFolderId()] : null;
362 3
			$folders[] = $mailbox->getListArray($this->getId(), $s);
363 3
		}
364 3
		$delimiter = reset($folders)['delimiter'];
365
		return [
366 3
			'id' => $this->getId(),
367 3
			'email' => $this->getEMailAddress(),
368 3
			'folders' => array_values($folders),
369 3
			'specialFolders' => $this->getSpecialFoldersIds(),
370 3
			'delimiter' => $delimiter,
371 3
		];
372
	}
373
374
	/**
375
	 * @return Horde_Mail_Transport
376
	 */
377
	public function createTransport() {
378
		$transport = $this->config->getSystemValue('app.mail.transport', 'smtp');
379
		if ($transport === 'php-mail') {
380
			return new Horde_Mail_Transport_Mail();
381
		}
382
383
		$password = $this->account->getOutboundPassword();
384
		$password = $this->crypto->decrypt($password);
385
		$params = [
386
			'host' => $this->account->getOutboundHost(),
387
			'password' => $password,
388
			'port' => $this->account->getOutboundPort(),
389
			'username' => $this->account->getOutboundUser(),
390
			'secure' => $this->convertSslMode($this->account->getOutboundSslMode()),
391
			'timeout' => 2
392
		];
393 View Code Duplication
		if ($this->config->getSystemValue('app.mail.smtplog.enabled', false)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
394
			$params['debug'] = $this->config->getSystemValue('datadirectory') . '/horde_smtp.log';
395
		}
396
		return new Horde_Mail_Transport_Smtphorde($params);
397
	}
398
399
	/**
400
	 * Lists special use folders for this account.
401
	 *
402
	 * The special uses returned are the "best" one for each special role,
403
	 * picked amongst the ones returned by the server, as well
404
	 * as the one guessed by our code.
405
	 *
406
	 * @param bool $base64_encode
407
	 * @return array In the form [<special use>=><folder id>, ...]
408
	 */
409 6
	public function getSpecialFoldersIds($base64_encode=true) {
410 6
		$folderRoles = ['inbox', 'sent', 'drafts', 'trash', 'archive', 'junk', 'flagged', 'all'];
411 6
		$specialFoldersIds = [];
412
413 6
		foreach ($folderRoles as $role) {
414 6
			$folders = $this->getSpecialFolder($role, true);
415 6
			$specialFoldersIds[$role] = (count($folders) === 0) ? null : $folders[0]->getFolderId();
416 6
			if ($specialFoldersIds[$role] !== null && $base64_encode === true) {
417 3
				$specialFoldersIds[$role] = base64_encode($specialFoldersIds[$role]);
418 3
			}
419 6
		}
420 6
		return $specialFoldersIds;
421
	}
422
423
	/**
424
	 * Get the "drafts" mailbox
425
	 *
426
	 * @return Mailbox The best candidate for the "drafts" inbox
427
	 */
428 View Code Duplication
	public function getDraftsFolder() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
429
		// check for existence
430
		$draftsFolder = $this->getSpecialFolder('drafts', true);
431
		if (count($draftsFolder) === 0) {
432
			// drafts folder does not exist - let's create one
433
			$conn = $this->getImapConnection();
434
			// TODO: also search for translated drafts mailboxes
435
			$conn->createMailbox('Drafts', [
436
				'special_use' => ['drafts'],
437
			]);
438
			return $this->guessBestMailBox($this->listMailboxes('Drafts'));
439
		}
440
		return $draftsFolder[0];
441
	}
442
443
	/**
444
	 * @return IMailBox
445
	 */
446
	public function getInbox() {
447
		$folders = $this->getSpecialFolder('inbox', false);
448
		return count($folders) > 0 ? $folders[0] : null;
449
	}
450
451
	/**
452
	 * Get the "sent mail" mailbox
453
	 *
454
	 * @return Mailbox The best candidate for the "sent mail" inbox
455
	 */
456 View Code Duplication
	public function getSentFolder() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
457
		//check for existence
458
		$sentFolders = $this->getSpecialFolder('sent', true);
459
		if (count($sentFolders) === 0) {
460
			//sent folder does not exist - let's create one
461
			$conn = $this->getImapConnection();
462
			//TODO: also search for translated sent mailboxes
463
			$conn->createMailbox('Sent', [
464
				'special_use' => ['sent'],
465
			]);
466
			return $this->guessBestMailBox($this->listMailboxes('Sent'));
467
		}
468
		return $sentFolders[0];
469
	}
470
471
	/**
472
	 * @param string $sourceFolderId
473
	 * @param int $messageId
474
	 */
475
	public function deleteMessage($sourceFolderId, $messageId) {
476
		$mb = $this->getMailbox($sourceFolderId);
477
		$hordeSourceMailBox = $mb->getHordeMailBox();
478
		// by default we will create a 'Trash' folder if no trash is found
479
		$trashId = "Trash";
480
		$createTrash = true;
481
482
		$trashFolders = $this->getSpecialFolder('trash', true);
483
484
		if (count($trashFolders) !== 0) {
485
			$trashId = $trashFolders[0]->getFolderId();
486
			$createTrash = false;
487
		} else {
488
			// no trash -> guess
489
			$trashes = array_filter($this->getMailboxes(), function($box) {
490
				/**
491
				 * @var Mailbox $box
492
				 */
493
				return (stripos($box->getDisplayName(), 'trash') !== false);
494
			});
495
			if (!empty($trashes)) {
496
				$trashId = array_values($trashes);
497
				$trashId = $trashId[0]->getFolderId();
498
				$createTrash = false;
499
			}
500
		}
501
502
		$hordeMessageIds = new Horde_Imap_Client_Ids($messageId);
503
		$hordeTrashMailBox = new Horde_Imap_Client_Mailbox($trashId);
504
505
		if ($sourceFolderId === $trashId) {
506
			$this->getImapConnection()->expunge($hordeSourceMailBox,
507
				array('ids' => $hordeMessageIds, 'delete' => true));
508
509
			\OC::$server->getLogger()->info("Message expunged: {message} from mailbox {mailbox}",
510
				array('message' => $messageId, 'mailbox' => $sourceFolderId));
511
		} else {
512
			$this->getImapConnection()->copy($hordeSourceMailBox, $hordeTrashMailBox,
513
				array('create' => $createTrash, 'move' => true, 'ids' => $hordeMessageIds));
514
515
			\OC::$server->getLogger()->info("Message moved to trash: {message} from mailbox {mailbox}",
516
				array('message' => $messageId, 'mailbox' => $sourceFolderId, 'app' => 'mail'));
517
		}
518
	}
519
520
	/**
521
	 * 
522
	 * @param int $messageId
523
	 */
524
	public function deleteDraft($messageId) {
0 ignored issues
show
Unused Code introduced by
The parameter $messageId is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
525
		$draftsFolder = $this->getDraftsFolder();
526
		
527
		$draftsMailBox = new \Horde_Imap_Client_Mailbox($draftsFolder->getFolderId(), false);
528
		$this->getImapConnection()->expunge($draftsMailBox);
529
	}
530
531
	/**
532
	 * Get 'best' mailbox guess
533
	 *
534
	 * For now the best candidate is the one with
535
	 * the most messages in it.
536
	 *
537
	 * @param array $folders
538
	 * @return Mailbox
539
	 */
540
	protected function guessBestMailBox(array $folders) {
541
		$maxMessages = -1;
542
		$bestGuess = null;
543
		foreach ($folders as $folder) {
544
			/** @var Mailbox $folder */
545
			if ($folder->getTotalMessages() > $maxMessages) {
546
				$maxMessages = $folder->getTotalMessages();
547
				$bestGuess = $folder;
548
			}
549
		}
550
		return $bestGuess;
551
	}
552
553
	/**
554
	 * Get mailbox(es) that have the given special use role
555
	 *
556
	 * With this method we can get a list of all mailboxes that have been
557
	 * determined to have a specific special use role. It can also return
558
	 * the best candidate for this role, for situations where we want
559
	 * one single folder.
560
	 *
561
	 * @param string $role Special role of the folder we want to get ('sent', 'inbox', etc.)
562
	 * @param bool $guessBest If set to true, return only the folder with the most messages in it
563
	 *
564
	 * @return Mailbox[] if $guessBest is false, or Mailbox if $guessBest is true. Empty [] if no match.
565
	 */
566 6
	protected function getSpecialFolder($role, $guessBest=true) {
567
568 6
		$specialFolders = [];
569 6
		foreach ($this->getMailboxes() as $mailbox) {
570 6
			if ($role === $mailbox->getSpecialRole()) {
571 6
				$specialFolders[] = $mailbox;
572 6
			}
573 6
		}
574
575 6
		if ($guessBest === true && count($specialFolders) > 1) {
576
			return [$this->guessBestMailBox($specialFolders)];
0 ignored issues
show
Best Practice introduced by
The expression return array($this->gues...lBox($specialFolders)); seems to be an array, but some of its elements' types (null) are incompatible with the return type documented by OCA\Mail\Account::getSpecialFolder of type OCA\Mail\Mailbox[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
577
		} else {
578 6
			return $specialFolders;
579
		}
580
	}
581
582
	/**
583
	 *  Localizes the name of the special use folders
584
	 *
585
	 *  The display name of the best candidate folder for each special use
586
	 *  is localized to the user's language
587
	 */
588 6
	protected function localizeSpecialMailboxes() {
589
590 6
		$l = \OC::$server->getL10N('mail');
591
		$map = [
592
			// TRANSLATORS: translated mail box name
593 6
			'inbox'   => $l->t('Inbox'),
594
			// TRANSLATORS: translated mail box name
595 6
			'sent'    => $l->t('Sent'),
596
			// TRANSLATORS: translated mail box name
597 6
			'drafts'  => $l->t('Drafts'),
598
			// TRANSLATORS: translated mail box name
599 6
			'archive' => $l->t('Archive'),
600
			// TRANSLATORS: translated mail box name
601 6
			'trash'   => $l->t('Trash'),
602
			// TRANSLATORS: translated mail box name
603 6
			'junk'    => $l->t('Junk'),
604
			// TRANSLATORS: translated mail box name
605 6
			'all'     => $l->t('All'),
606
			// TRANSLATORS: translated mail box name
607 6
			'flagged' => $l->t('Favorites'),
608 6
		];
609 6
		$mailboxes = $this->getMailboxes();
610 6
		$specialIds = $this->getSpecialFoldersIds(false);
611 6
		foreach ($mailboxes as $i => $mailbox) {
612 6
			if (in_array($mailbox->getFolderId(), $specialIds) === true) {
613 6
				if (isset($map[$mailbox->getSpecialRole()])) {
614 6
					$translatedDisplayName = $map[$mailbox->getSpecialRole()];
615 6
					$mailboxes[$i]->setDisplayName((string)$translatedDisplayName);
616 6
				}
617 6
			}
618 6
		}
619 6
	}
620
621
	/**
622
	 * Sort mailboxes
623
	 *
624
	 * Sort the array of mailboxes with
625
	 *  - special use folders coming first in this order: all, inbox, flagged, drafts, sent, archive, junk, trash
626
	 *  - 'normal' folders coming after that, sorted alphabetically
627
	 */
628 6
	protected function sortMailboxes() {
629
630 6
		$mailboxes = $this->getMailboxes();
631
		usort($mailboxes, function($a, $b) {
632
			/**
633
			 * @var Mailbox $a
634
			 * @var Mailbox $b
635
			 */
636 6
			$roleA = $a->getSpecialRole();
637 6
			$roleB = $b->getSpecialRole();
638
			$specialRolesOrder = [
639 6
				'all'     => 0,
640 6
				'inbox'   => 1,
641 6
				'flagged' => 2,
642 6
				'drafts'  => 3,
643 6
				'sent'    => 4,
644 6
				'archive' => 5,
645 6
				'junk'    => 6,
646 6
				'trash'   => 7,
647 6
			];
648
			// if there is a flag unknown to us, we ignore it for sorting :
649
			// the folder will be sorted by name like any other 'normal' folder
650 6
			if (array_key_exists($roleA, $specialRolesOrder) === false) {
651 5
				$roleA = null;
652 5
			}
653 6
			if (array_key_exists($roleB, $specialRolesOrder) === false) {
654 6
				$roleB = null;
655 6
			}
656
657 6
			if ($roleA === null && $roleB !== null) {
658 5
				return 1;
659 6
			} elseif ($roleA !== null && $roleB === null) {
660 6
				return -1;
661 6
			} elseif ($roleA !== null && $roleB !== null) {
662 6
				if ($roleA === $roleB) {
663
					return strcasecmp($a->getdisplayName(), $b->getDisplayName());
664
				} else {
665 6
					return $specialRolesOrder[$roleA] - $specialRolesOrder[$roleB];
666
				}
667
			}
668
			// we get here if $roleA === null && $roleB === null
669 5
			return strcasecmp($a->getDisplayName(), $b->getDisplayName());
670 6
		});
671
672 6
		$this->mailboxes = $mailboxes;
673 6
	}
674
675
	/**
676
	 * Convert special security mode values into Horde parameters
677
	 *
678
	 * @param string $sslMode
679
	 * @return false|string
680
	 */
681
	protected function convertSslMode($sslMode) {
682
		switch ($sslMode) {
683
			case 'none':
684
				return false;
685
		}
686
		return $sslMode;
687
	}
688
689
	/**
690
	 * @param $query
691
	 * @return array
692
	 */
693 4
	public function getChangedMailboxes($query) {
694 4
		$imp = $this->getImapConnection();
695 4
		$allBoxes = $this->getMailboxes();
696 4
		$allBoxesMap = [];
697 4
		foreach ($allBoxes as $mb) {
698 4
			$allBoxesMap[$mb->getFolderId()] = $mb;
699 4
		}
700
701
		// filter non existing mailboxes
702
		$mailBoxNames = array_filter(array_keys($query), function($folderId) use ($allBoxesMap) {
703 4
			return isset($allBoxesMap[$folderId]);
704 4
		});
705
706 4
		$status = $imp->status($mailBoxNames);
707
708
		// filter for changed mailboxes
709 4
		$changedBoxes = [];
710 4
		foreach ($status as $folderId => $s) {
711 3
			$uidValidity = $query[$folderId]['uidvalidity'];
712 3
			$uidNext = $query[$folderId]['uidnext'];
713
714 3
			if ($uidNext === $s['uidnext'] &&
715 3
				$uidValidity === $s['uidvalidity']) {
716 3
				continue;
717
			}
718
			// get unread messages
719 3
			if (isset($allBoxesMap[$folderId])) {
720
				/** @var Mailbox $m */
721 3
				$m = $allBoxesMap[$folderId];
722 3
				$role = $m->getSpecialRole();
723 3
				if (is_null($role) || $role === 'inbox') {
724 3
					$newMessages = $m->getMessagesSince($uidNext, $s['uidnext']);
725
					// only trigger updates in case new messages are actually available
726 3
					if (!empty($newMessages)) {
727 3
						$changedBoxes[$folderId] = $m->getListArray($this->getId(), $s);
728 3
						$changedBoxes[$folderId]['messages'] = $newMessages;
729 3
						$newUnreadMessages = array_filter($newMessages, function($m) {
730 3
							return $m['flags']['unseen'];
731 3
						});
732 3
						$changedBoxes[$folderId]['newUnReadCounter'] = count($newUnreadMessages);
733 3
					}
734 3
				}
735 3
			}
736 4
		}
737
738 4
		return $changedBoxes;
739
	}
740
741
	/**
742
	 * @throws \Horde_Imap_Client_Exception
743
	 */
744
	public function reconnect() {
745
		$this->mailboxes = null;
746
		if ($this->client) {
747
			$this->client->close();
748
			$this->client = null;
749
		}
750
		$this->getImapConnection();
751
	}
752
753
	/**
754
	 * @return array
755
	 */
756
	public function getConfiguration() {
757
		return $this->account->toJson();
758
	}
759
760
	/**
761
	 * @return string|Horde_Mail_Rfc822_List
762
	 */
763
	public function getEmail() {
764
		return $this->account->getEmail();
765
	}
766
767
	public function testConnectivity() {
768
		// connect to imap
769
		$this->getImapConnection();
770
771
		// connect to smtp
772
		$smtp = $this->createTransport();
773
		if ($smtp instanceof Horde_Mail_Transport_Smtphorde) {
0 ignored issues
show
Bug introduced by
The class Horde_Mail_Transport_Smtphorde does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
774
			$smtp->getSMTPObject();
775
		}
776
	}
777
778
	/**
779
	 * Factory method for creating new messages
780
	 *
781
	 * @return IMessage
782
	 */
783
	public function newMessage() {
784
		return new Message();
785
	}
786
787
	/**
788
	 * Factory method for creating new reply messages
789
	 *
790
	 * @return ReplyMessage
791
	 */
792
	public function newReplyMessage() {
793
		return new ReplyMessage();
794
	}
795
796
}
797