Completed
Pull Request — master (#1272)
by Christoph
03:31
created

Account::getSpecialFoldersIds()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30
Metric Value
dl 0
loc 13
ccs 0
cts 11
cp 0
rs 8.8571
cc 5
eloc 9
nc 5
nop 1
crap 30
1
<?php
2
3
/**
4
 * Copyright (c) 2012 Bart Visscher <[email protected]>
5
 * Copyright (c) 2014 Thomas Müller <[email protected]>
6
 * Copyright (c) 2015 Christoph Wurst <[email protected]>
7
 *
8
 * This file is licensed under the Affero General Public License version 3 or
9
 * later.
10
 * See the COPYING-README file.
11
 */
12
13
namespace OCA\Mail;
14
15
use Horde_Imap_Client_Ids;
16
use Horde_Imap_Client_Mailbox;
17
use Horde_Imap_Client_Socket;
18
use Horde_Imap_Client;
19
use Horde_Mail_Rfc822_Address;
20
use Horde_Mail_Transport;
21
use Horde_Mail_Transport_Mail;
22
use Horde_Mail_Transport_Smtphorde;
23
use Horde_Mime_Headers_Date;
24
use Horde_Mime_Mail;
25
use Horde_Mime_Part;
26
use OCA\Mail\Cache\Cache;
27
use OCA\Mail\Db\MailAccount;
28
use OCA\Mail\Model\IMessage;
29
use OCA\Mail\Model\Message;
30
use OCA\Mail\Model\ReplyMessage;
31
use OCA\Mail\Service\IAccount;
32
use OCA\Mail\Service\IMailBox;
33
use OCP\IConfig;
34
use OCP\ICacheFactory;
35
use OCP\Security\ICrypto;
36
37
class Account implements IAccount {
38
39
	/** @var MailAccount */
40
	private $account;
41
42
	/** @var Mailbox[]|null */
43
	private $mailboxes;
44
45
	/** @var Horde_Imap_Client_Socket */
46
	private $client;
47
48
	/** @var ICrypto */
49
	private $crypto;
50
51
	/** @var IConfig */
52
	private $config;
53
54
	/** @var ICacheFactory */
55
	private $memcacheFactory;
56
57
	/**
58
	 * @param MailAccount $account
59
	 */
60 3
	public function __construct(MailAccount $account) {
61 3
		$this->account = $account;
62 3
		$this->mailboxes = null;
63 3
		$this->crypto = \OC::$server->getCrypto();
64 3
		$this->config = \OC::$server->getConfig();
65 3
		$this->memcacheFactory = \OC::$server->getMemcacheFactory();
66 3
	}
67
68
	/**
69
	 * @return int
70
	 */
71
	public function getId() {
72
		return $this->account->getId();
73
	}
74
75
	/**
76
	 * @return string
77
	 */
78
	public function getName() {
79
		return $this->account->getName();
80
	}
81
82
	/**
83
	 * @return string
84
	 */
85
	public function getEMailAddress() {
86
		return $this->account->getEmail();
87
	}
88
89
	/**
90
	 * @return Horde_Imap_Client_Socket
91
	 */
92
	public function getImapConnection() {
93
		if (is_null($this->client)) {
94
			$host = $this->account->getInboundHost();
95
			$user = $this->account->getInboundUser();
96
			$password = $this->account->getInboundPassword();
97
			$password = $this->crypto->decrypt($password);
98
			$port = $this->account->getInboundPort();
99
			$ssl_mode = $this->convertSslMode($this->account->getInboundSslMode());
100
101
			$params = [
102
				'username' => $user,
103
				'password' => $password,
104
				'hostspec' => $host,
105
				'port' => $port,
106
				'secure' => $ssl_mode,
107
				'timeout' => 20,
108
			];
109
			if ($this->config->getSystemValue('app.mail.imaplog.enabled', false)) {
110
				$params['debug'] = $this->config->getSystemValue('datadirectory') . '/horde.log';
111
			}
112
			if ($this->config->getSystemValue('app.mail.server-side-cache.enabled', false)) {
113
				if ($this->memcacheFactory->isAvailable()) {
114
					$params['cache'] = [
115
						'backend' => new Cache(array(
116
							'cacheob' => $this->memcacheFactory
117
								->create(md5($this->getId() . $this->getEMailAddress()))
118
						))];
119
				}
120
			}
121
			$this->client = new \Horde_Imap_Client_Socket($params);
122
			$this->client->login();
123
		}
124
		return $this->client;
125
	}
126
127
	/**
128
	 * @param string $mailBox
129
	 * @return Mailbox
130
	 */
131
	public function createMailbox($mailBox) {
132
		$conn = $this->getImapConnection();
133
		$conn->createMailbox($mailBox);
134
		$this->mailboxes = null;
135
136
		return $this->getMailbox($mailBox);
137
	}
138
139
	/**
140
	 * Send a new message or reply to an existing message
141
	 *
142
	 * @param IMessage $message
143
	 * @param int|null $draftUID
144
	 */
145
	public function sendMessage(IMessage $message, $draftUID) {
146
		// build mime body
147
		$from = new Horde_Mail_Rfc822_Address($message->getFrom());
148
		$from->personal = $this->getName();
149
		$headers = [
150
			'From' => $from,
151
			'To' => $message->getToList(),
152
			'Cc' => $message->getCCList(),
153
			'Bcc' => $message->getBCCList(),
154
			'Subject' => $message->getSubject(),
155
		];
156
157
		if (!is_null($message->getRepliedMessage())) {
158
			$headers['In-Reply-To'] = $message->getRepliedMessage()->getMessageId();
159
		}
160
161
		$mail = new Horde_Mime_Mail();
162
		$mail->addHeaders($headers);
163
		$mail->setBody($message->getContent());
164
165
		// Append attachments
166
		foreach ($message->getAttachments() as $attachment) {
167
			$mail->addMimePart($attachment);
168
		}
169
170
		// Send the message
171
		$transport = $this->createTransport();
172
		$mail->send($transport);
173
174
		// Save the message in the sent folder
175
		$sentFolder = $this->getSentFolder();
176
		/** @var resource $raw */
177
		$raw = stream_get_contents($mail->getRaw());
178
		$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...
179
			Horde_Imap_Client::FLAG_SEEN
180
		]);
181
182
		// Delete draft if one exists
183
		if (!is_null($draftUID)) {
184
			$draftsFolder = $this->getDraftsFolder();
185
			$draftsFolder->setMessageFlag($draftUID, Horde_Imap_Client::FLAG_DELETED, true);
186
			$this->deleteDraft($draftUID);
187
		}
188
	}
189
190
	/**
191
	 * @param IMessage $message
192
	 * @param int|null $previousUID
193
	 * @return int
194
	 */
195
	public function saveDraft(IMessage $message, $previousUID) {
196
		// build mime body
197
		$from = new Horde_Mail_Rfc822_Address($message->getFrom());
198
		$from->personal = $this->getName();
199
		$headers = [
200
			'From' => $from,
201
			'To' => $message->getToList(),
202
			'Cc' => $message->getCCList(),
203
			'Bcc' => $message->getBCCList(),
204
			'Subject' => $message->getSubject(),
205
			'Date' => Horde_Mime_Headers_Date::create(),
206
		];
207
208
		$mail = new Horde_Mime_Mail();
209
		$mail->addHeaders($headers);
210
		$body = new Horde_Mime_Part();
211
		$body->setType('text/plain');
212
		$body->setContents($message->getContent());
213
		$mail->setBasePart($body);
214
215
		// create transport and save message
216
		// save the message in the drafts folder
217
		$draftsFolder = $this->getDraftsFolder();
218
		/** @var resource $raw */
219
		$raw = $mail->getRaw();
220
		$newUid = $draftsFolder->saveDraft(stream_get_contents($raw));
221
222
		// delete old version if one exists
223
		if (!is_null($previousUID)) {
224
			$draftsFolder->setMessageFlag($previousUID, \Horde_Imap_Client::FLAG_DELETED,
225
				true);
226
			$this->deleteDraft($previousUID);
227
		}
228
229
		return $newUid;
230
	}
231
232
	/**
233
	 * @param string $mailBox
234
	 */
235
	public function deleteMailbox($mailBox) {
236
		if ($mailBox instanceof Mailbox) {
237
			$mailBox = $mailBox->getFolderId();
238
		}
239
		$conn = $this->getImapConnection();
240
		$conn->deleteMailbox($mailBox);
241
		$this->mailboxes = null;
242
	}
243
244
	/**
245
	 * Lists mailboxes (folders) for this account.
246
	 *
247
	 * Lists mailboxes and also queries the server for their 'special use',
248
	 * eg. inbox, sent, trash, etc
249
	 *
250
	 * @param string $pattern Pattern to match mailboxes against. All by default.
251
	 * @return Mailbox[]
252
	 */
253
	protected function listMailboxes($pattern = '*') {
254
		// open the imap connection
255
		$conn = $this->getImapConnection();
256
257
		// if successful -> get all folders of that account
258
		$mailBoxes = $conn->listMailboxes($pattern, Horde_Imap_Client::MBOX_ALL,
259
			[
260
			'delimiter' => true,
261
			'attributes' => true,
262
			'special_use' => true,
263
			'sort' => true
264
		]);
265
266
		$mailboxes = [];
267
		foreach ($mailBoxes as $mailbox) {
268
			$mailboxes[] = new Mailbox($conn, $mailbox['mailbox'],
269
				$mailbox['attributes'], $mailbox['delimiter']);
270
			if ($mailbox['mailbox']->utf8 === 'INBOX') {
271
				$mailboxes[] = new SearchMailbox($conn, $mailbox['mailbox'],
272
					$mailbox['attributes'], $mailbox['delimiter']);
273
			}
274
		}
275
276
		return $mailboxes;
277
	}
278
279
	/**
280
	 * @param string $folderId
281
	 * @return \OCA\Mail\Mailbox
282
	 */
283
	public function getMailbox($folderId) {
284
		$conn = $this->getImapConnection();
285
		$parts = explode('/', $folderId);
286
		if (count($parts) > 1 && $parts[1] === 'FLAGGED') {
287
			$mailbox = new Horde_Imap_Client_Mailbox($parts[0]);
288
			return new SearchMailbox($conn, $mailbox, []);
289
		}
290
		$mailbox = new Horde_Imap_Client_Mailbox($folderId);
291
		return new Mailbox($conn, $mailbox, []);
292
	}
293
294
	/**
295
	 * Get a list of all mailboxes in this account
296
	 *
297
	 * @return Mailbox[]
298
	 */
299
	public function getMailboxes() {
300
		if ($this->mailboxes === null) {
301
			$this->mailboxes = $this->listMailboxes();
302
			$this->sortMailboxes();
303
			$this->localizeSpecialMailboxes();
304
		}
305
306
		return $this->mailboxes;
307
	}
308
309
	/**
310
	 * @return array
311
	 */
312
	public function getListArray() {
313
314
		$folders = [];
315
		$mailBoxes = $this->getMailboxes();
316
		$mailBoxNames = array_map(function($mb) {
317
			/** @var Mailbox $mb */
318
			return $mb->getFolderId();
319
		}, array_filter($mailBoxes, function($mb) {
320
			/** @var Mailbox $mb */
321
			return (!$mb instanceof SearchMailbox) && (!in_array('\noselect', $mb->attributes()));
322
		}));
323
324
		$status = $this->getImapConnection()->status($mailBoxNames);
325
		foreach ($mailBoxes as $mailbox) {
326
			$s = isset($status[$mailbox->getFolderId()]) ? $status[$mailbox->getFolderId()] : null;
327
			$folders[] = $mailbox->getListArray($this->getId(), $s);
328
		}
329
		$delimiter = reset($folders)['delimiter'];
330
		return [
331
			'id' => $this->getId(),
332
			'email' => $this->getEMailAddress(),
333
			'folders' => array_values($folders),
334
			'specialFolders' => $this->getSpecialFoldersIds(),
335
			'delimiter' => $delimiter,
336
		];
337
	}
338
339
	/**
340
	 * @return Horde_Mail_Transport
341
	 */
342
	public function createTransport() {
343
		$transport = $this->config->getSystemValue('app.mail.transport', 'smtp');
344
		if ($transport === 'php-mail') {
345
			return new Horde_Mail_Transport_Mail();
346
		}
347
348
		$password = $this->account->getOutboundPassword();
349
		$password = $this->crypto->decrypt($password);
350
		$params = [
351
			'host' => $this->account->getOutboundHost(),
352
			'password' => $password,
353
			'port' => $this->account->getOutboundPort(),
354
			'username' => $this->account->getOutboundUser(),
355
			'secure' => $this->convertSslMode($this->account->getOutboundSslMode()),
356
			'timeout' => 2
357
		];
358
		return new Horde_Mail_Transport_Smtphorde($params);
359
	}
360
361
	/**
362
	 * Lists special use folders for this account.
363
	 *
364
	 * The special uses returned are the "best" one for each special role,
365
	 * picked amongst the ones returned by the server, as well
366
	 * as the one guessed by our code.
367
	 *
368
	 * @param bool $base64_encode
369
	 * @return array In the form [<special use>=><folder id>, ...]
370
	 */
371
	public function getSpecialFoldersIds($base64_encode=true) {
372
		$folderRoles = ['inbox', 'sent', 'drafts', 'trash', 'archive', 'junk', 'flagged', 'all'];
373
		$specialFoldersIds = [];
374
375
		foreach ($folderRoles as $role) {
376
			$folders = $this->getSpecialFolder($role, true);
377
			$specialFoldersIds[$role] = (count($folders) === 0) ? null : $folders[0]->getFolderId();
378
			if ($specialFoldersIds[$role] !== null && $base64_encode === true) {
379
				$specialFoldersIds[$role] = base64_encode($specialFoldersIds[$role]);
380
			}
381
		}
382
		return $specialFoldersIds;
383
	}
384
385
	/**
386
	 * Get the "drafts" mailbox
387
	 *
388
	 * @return Mailbox The best candidate for the "drafts" inbox
389
	 */
390 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...
391
		// check for existence
392
		$draftsFolder = $this->getSpecialFolder('drafts', true);
393
		if (count($draftsFolder) === 0) {
394
			// drafts folder does not exist - let's create one
395
			$conn = $this->getImapConnection();
396
			// TODO: also search for translated drafts mailboxes
397
			$conn->createMailbox('Drafts', [
398
				'special_use' => ['drafts'],
399
			]);
400
			return $this->guessBestMailBox($this->listMailboxes('Drafts'));
401
		}
402
		return $draftsFolder[0];
403
	}
404
405
	/**
406
	 * @return IMailBox
407
	 */
408
	public function getInbox() {
409
		$folders = $this->getSpecialFolder('inbox', false);
410
		return count($folders) > 0 ? $folders[0] : null;
411
	}
412
413
	/**
414
	 * Get the "sent mail" mailbox
415
	 *
416
	 * @return Mailbox The best candidate for the "sent mail" inbox
417
	 */
418 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...
419
		//check for existence
420
		$sentFolders = $this->getSpecialFolder('sent', true);
421
		if (count($sentFolders) === 0) {
422
			//sent folder does not exist - let's create one
423
			$conn = $this->getImapConnection();
424
			//TODO: also search for translated sent mailboxes
425
			$conn->createMailbox('Sent', [
426
				'special_use' => ['sent'],
427
			]);
428
			return $this->guessBestMailBox($this->listMailboxes('Sent'));
429
		}
430
		return $sentFolders[0];
431
	}
432
433
	/**
434
	 * @param string $sourceFolderId
435
	 * @param int $messageId
436
	 */
437
	public function deleteMessage($sourceFolderId, $messageId) {
438
		$mb = $this->getMailbox($sourceFolderId);
439
		$hordeSourceMailBox = $mb->getHordeMailBox();
440
		// by default we will create a 'Trash' folder if no trash is found
441
		$trashId = "Trash";
442
		$createTrash = true;
443
444
		$trashFolders = $this->getSpecialFolder('trash', true);
445
446
		if (count($trashFolders) !== 0) {
447
			$trashId = $trashFolders[0]->getFolderId();
448
			$createTrash = false;
449
		} else {
450
			// no trash -> guess
451
			$trashes = array_filter($this->getMailboxes(), function($box) {
452
				/**
453
				 * @var Mailbox $box
454
				 */
455
				return (stripos($box->getDisplayName(), 'trash') !== false);
456
			});
457
			if (!empty($trashes)) {
458
				$trashId = array_values($trashes);
459
				$trashId = $trashId[0]->getFolderId();
460
				$createTrash = false;
461
			}
462
		}
463
464
		$hordeMessageIds = new Horde_Imap_Client_Ids($messageId);
465
		$hordeTrashMailBox = new Horde_Imap_Client_Mailbox($trashId);
466
467
		if ($sourceFolderId === $trashId) {
468
			$this->getImapConnection()->expunge($hordeSourceMailBox,
469
				array('ids' => $hordeMessageIds, 'delete' => true));
470
471
			\OC::$server->getLogger()->info("Message expunged: {message} from mailbox {mailbox}",
472
				array('message' => $messageId, 'mailbox' => $sourceFolderId));
473
		} else {
474
			$this->getImapConnection()->copy($hordeSourceMailBox, $hordeTrashMailBox,
475
				array('create' => $createTrash, 'move' => true, 'ids' => $hordeMessageIds));
476
477
			\OC::$server->getLogger()->info("Message moved to trash: {message} from mailbox {mailbox}",
478
				array('message' => $messageId, 'mailbox' => $sourceFolderId, 'app' => 'mail'));
479
		}
480
	}
481
482
	/**
483
	 * 
484
	 * @param int $messageId
485
	 */
486
	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...
487
		$draftsFolder = $this->getDraftsFolder();
488
		
489
		$draftsMailBox = new \Horde_Imap_Client_Mailbox($draftsFolder->getFolderId(), false);
490
		$this->getImapConnection()->expunge($draftsMailBox);
491
	}
492
493
	/**
494
	 * Get 'best' mailbox guess
495
	 *
496
	 * For now the best candidate is the one with
497
	 * the most messages in it.
498
	 *
499
	 * @param array $folders
500
	 * @return Mailbox
501
	 */
502
	protected function guessBestMailBox(array $folders) {
503
		$maxMessages = -1;
504
		$bestGuess = null;
505
		foreach ($folders as $folder) {
506
			/** @var Mailbox $folder */
507
			if ($folder->getTotalMessages() > $maxMessages) {
508
				$maxMessages = $folder->getTotalMessages();
509
				$bestGuess = $folder;
510
			}
511
		}
512
		return $bestGuess;
513
	}
514
515
	/**
516
	 * Get mailbox(es) that have the given special use role
517
	 *
518
	 * With this method we can get a list of all mailboxes that have been
519
	 * determined to have a specific special use role. It can also return
520
	 * the best candidate for this role, for situations where we want
521
	 * one single folder.
522
	 *
523
	 * @param string $role Special role of the folder we want to get ('sent', 'inbox', etc.)
524
	 * @param bool $guessBest If set to true, return only the folder with the most messages in it
525
	 *
526
	 * @return Mailbox[] if $guessBest is false, or Mailbox if $guessBest is true. Empty [] if no match.
527
	 */
528
	protected function getSpecialFolder($role, $guessBest=true) {
529
530
		$specialFolders = [];
531
		foreach ($this->getMailboxes() as $mailbox) {
532
			if ($role === $mailbox->getSpecialRole()) {
533
				$specialFolders[] = $mailbox;
534
			}
535
		}
536
537
		if ($guessBest === true && count($specialFolders) > 1) {
538
			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...
539
		} else {
540
			return $specialFolders;
541
		}
542
	}
543
544
	/**
545
	 *  Localizes the name of the special use folders
546
	 *
547
	 *  The display name of the best candidate folder for each special use
548
	 *  is localized to the user's language
549
	 */
550
	protected function localizeSpecialMailboxes() {
551
552
		$l = \OC::$server->getL10N('mail');
553
		$map = [
554
			// TRANSLATORS: translated mail box name
555
			'inbox'   => $l->t('Inbox'),
556
			// TRANSLATORS: translated mail box name
557
			'sent'    => $l->t('Sent'),
558
			// TRANSLATORS: translated mail box name
559
			'drafts'  => $l->t('Drafts'),
560
			// TRANSLATORS: translated mail box name
561
			'archive' => $l->t('Archive'),
562
			// TRANSLATORS: translated mail box name
563
			'trash'   => $l->t('Trash'),
564
			// TRANSLATORS: translated mail box name
565
			'junk'    => $l->t('Junk'),
566
			// TRANSLATORS: translated mail box name
567
			'all'     => $l->t('All'),
568
			// TRANSLATORS: translated mail box name
569
			'flagged' => $l->t('Favorites'),
570
		];
571
		$mailboxes = $this->getMailboxes();
572
		$specialIds = $this->getSpecialFoldersIds(false);
573
		foreach ($mailboxes as $i => $mailbox) {
574
			if (in_array($mailbox->getFolderId(), $specialIds) === true) {
575
				if (isset($map[$mailbox->getSpecialRole()])) {
576
					$translatedDisplayName = $map[$mailbox->getSpecialRole()];
577
					$mailboxes[$i]->setDisplayName((string)$translatedDisplayName);
578
				}
579
			}
580
		}
581
	}
582
583
	/**
584
	 * Sort mailboxes
585
	 *
586
	 * Sort the array of mailboxes with
587
	 *  - special use folders coming first in this order: all, inbox, flagged, drafts, sent, archive, junk, trash
588
	 *  - 'normal' folders coming after that, sorted alphabetically
589
	 */
590
	protected function sortMailboxes() {
591
592
		$mailboxes = $this->getMailboxes();
593
		usort($mailboxes, function($a, $b) {
594
			/**
595
			 * @var Mailbox $a
596
			 * @var Mailbox $b
597
			 */
598
			$roleA = $a->getSpecialRole();
599
			$roleB = $b->getSpecialRole();
600
			$specialRolesOrder = [
601
				'all'     => 0,
602
				'inbox'   => 1,
603
				'flagged' => 2,
604
				'drafts'  => 3,
605
				'sent'    => 4,
606
				'archive' => 5,
607
				'junk'    => 6,
608
				'trash'   => 7,
609
			];
610
			// if there is a flag unknown to us, we ignore it for sorting :
611
			// the folder will be sorted by name like any other 'normal' folder
612
			if (array_key_exists($roleA, $specialRolesOrder) === false) {
613
				$roleA = null;
614
			}
615
			if (array_key_exists($roleB, $specialRolesOrder) === false) {
616
				$roleB = null;
617
			}
618
619
			if ($roleA === null && $roleB !== null) {
620
				return 1;
621
			} elseif ($roleA !== null && $roleB === null) {
622
				return -1;
623
			} elseif ($roleA !== null && $roleB !== null) {
624
				if ($roleA === $roleB) {
625
					return strcasecmp($a->getdisplayName(), $b->getDisplayName());
626
				} else {
627
					return $specialRolesOrder[$roleA] - $specialRolesOrder[$roleB];
628
				}
629
			}
630
			// we get here if $roleA === null && $roleB === null
631
			return strcasecmp($a->getDisplayName(), $b->getDisplayName());
632
		});
633
634
		$this->mailboxes = $mailboxes;
635
	}
636
637
	/**
638
	 * Convert special security mode values into Horde parameters
639
	 *
640
	 * @param string $sslMode
641
	 * @return false|string
642
	 */
643
	protected function convertSslMode($sslMode) {
644
		switch ($sslMode) {
645
			case 'none':
646
				return false;
647
		}
648
		return $sslMode;
649
	}
650
651
	/**
652
	 * @param $query
653
	 * @return array
654
	 */
655
	public function getChangedMailboxes($query) {
656
		$imp = $this->getImapConnection();
657
		$allBoxes = $this->getMailboxes();
658
		$allBoxesMap = [];
659
		foreach ($allBoxes as $mb) {
660
			$allBoxesMap[$mb->getFolderId()] = $mb;
661
		}
662
663
		// filter non existing mailboxes
664
		$mailBoxNames = array_filter(array_keys($query), function($folderId) use ($allBoxesMap) {
665
			return isset($allBoxesMap[$folderId]);
666
		});
667
668
		$status = $imp->status($mailBoxNames);
669
670
		// filter for changed mailboxes
671
		$changedBoxes = [];
672
		foreach ($status as $folderId => $s) {
673
			$uidValidity = $query[$folderId]['uidvalidity'];
674
			$uidNext = $query[$folderId]['uidnext'];
675
676
			if ($uidNext === $s['uidnext'] &&
677
				$uidValidity === $s['uidvalidity']) {
678
				continue;
679
			}
680
			// get unread messages
681
			if (isset($allBoxesMap[$folderId])) {
682
				/** @var Mailbox $m */
683
				$m = $allBoxesMap[$folderId];
684
				$role = $m->getSpecialRole();
685
				if (is_null($role) || $role === 'inbox') {
686
					$newMessages = $m->getMessagesSince($uidNext, $s['uidnext']);
687
					// only trigger updates in case new messages are actually available
688
					if (!empty($newMessages)) {
689
						$changedBoxes[$folderId] = $m->getListArray($this->getId(), $s);
690
						$changedBoxes[$folderId]['messages'] = $newMessages;
691
						$newUnreadMessages = array_filter($newMessages, function($m) {
692
							return $m['flags']['unseen'];
693
						});
694
						$changedBoxes[$folderId]['newUnReadCounter'] = count($newUnreadMessages);
695
					}
696
				}
697
			}
698
		}
699
700
		return $changedBoxes;
701
	}
702
703
	/**
704
	 * @throws \Horde_Imap_Client_Exception
705
	 */
706
	public function reconnect() {
707
		$this->mailboxes = null;
708
		if ($this->client) {
709
			$this->client->close();
710
			$this->client = null;
711
		}
712
		$this->getImapConnection();
713
	}
714
715
	/**
716
	 * @return array
717
	 */
718
	public function getConfiguration() {
719
		return $this->account->toJson();
720
	}
721
722
	/**
723
	 * @return string|Horde_Mail_Rfc822_List
724
	 */
725
	public function getEmail() {
726
		return $this->account->getEmail();
727
	}
728
729
	public function testConnectivity() {
730
		// connect to imap
731
		$this->getImapConnection();
732
733
		// connect to smtp
734
		$smtp = $this->createTransport();
735
		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...
736
			$smtp->getSMTPObject();
737
		}
738
	}
739
740
	/**
741
	 * Factory method for creating new messages
742
	 *
743
	 * @return OCA\Mail\Model\IMessage
744
	 */
745
	public function newMessage() {
746
		return new Message();
747
	}
748
749
	/**
750
	 * Factory method for creating new reply messages
751
	 *
752
	 * @return OCA\Mail\Model\ReplyMessage
753
	 */
754
	public function newReplyMessage() {
755
		return new ReplyMessage();
756
	}
757
758
}
759