Completed
Pull Request — master (#1167)
by Christoph
55:31
created

Account::getDraftsFolder()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 14
Ratio 100 %

Code Coverage

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