Completed
Pull Request — master (#1192)
by Christoph
06:02
created

Account::newReplyMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
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 Horde_Imap_Client_Mailbox */
43
	private $mailbox;
44
45
	/** @var Mailbox[]|null */
46
	private $mailboxes;
47
48
	/** @var Horde_Imap_Client_Socket */
49
	private $client;
50
51
	/** @var ICrypto */
52
	private $crypto;
53
54
	/** @var IConfig */
55
	private $config;
56
57
	/** @var ICacheFactory */
58
	private $memcacheFactory;
59
60
	/**
61
	 * @param MailAccount $account
62
	 */
63 3
	public function __construct(MailAccount $account) {
64 3
		$this->account = $account;
65 3
		$this->mailboxes = null;
66 3
		$this->crypto = \OC::$server->getCrypto();
67 3
		$this->config = \OC::$server->getConfig();
68 3
		$this->memcacheFactory = \OC::$server->getMemcacheFactory();
69 3
	}
70
71
	/**
72
	 * @return int
73
	 */
74 3
	public function getId() {
75 3
		return $this->account->getId();
76
	}
77
78
	/**
79
	 * @return string
80
	 */
81
	public function getName() {
82
		return $this->account->getName();
83
	}
84
85
	/**
86
	 * @return string
87
	 */
88 3
	public function getEMailAddress() {
89 3
		return $this->account->getEmail();
90
	}
91
92
	/**
93
	 * @return Horde_Imap_Client_Socket
94
	 */
95 13
	public function getImapConnection() {
96 13
		if (is_null($this->client)) {
97
			$host = $this->account->getInboundHost();
98
			$user = $this->account->getInboundUser();
99
			$password = $this->account->getInboundPassword();
100
			$password = $this->crypto->decrypt($password);
101
			$port = $this->account->getInboundPort();
102
			$ssl_mode = $this->convertSslMode($this->account->getInboundSslMode());
103
104
			$params = [
105
				'username' => $user,
106
				'password' => $password,
107
				'hostspec' => $host,
108
				'port' => $port,
109
				'secure' => $ssl_mode,
110
				'timeout' => 20,
111
			];
112
			if ($this->config->getSystemValue('app.mail.imaplog.enabled', false)) {
113
				$params['debug'] = $this->config->getSystemValue('datadirectory') . '/horde.log';
114
			}
115
			if ($this->config->getSystemValue('app.mail.server-side-cache.enabled', false)) {
116
				if ($this->memcacheFactory->isAvailable()) {
117
					$params['cache'] = [
118
						'backend' => new Cache(array(
119
							'cacheob' => $this->memcacheFactory
120
								->create(md5($this->getId() . $this->getEMailAddress()))
121
						))];
122
				}
123
			}
124
			$this->client = new \Horde_Imap_Client_Socket($params);
125
			$this->client->login();
126
		}
127 13
		return $this->client;
128
	}
129
130
	/**
131
	 * @param string $mailBox
132
	 * @return Mailbox
133
	 */
134 12
	public function createMailbox($mailBox) {
135 12
		$conn = $this->getImapConnection();
136 12
		$conn->createMailbox($mailBox);
137 12
		$this->mailboxes = null;
138
139 12
		return $this->getMailbox($mailBox);
140
	}
141
142
	/**
143
	 * Send a new message or reply to an existing message
144
	 *
145
	 * @param IMessage $message
146
	 * @param int|null $draftUID
147
	 */
148 4
	public function sendMessage(IMessage $message, $draftUID) {
149
		// build mime body
150
		$from = new Horde_Mail_Rfc822_Address($message->getFrom());
151
		$from->personal = $this->getName();
152
		$headers = [
153
			'From' => $from,
154
			'To' => $message->getToList(),
155
			'Cc' => $message->getCCList(),
156
			'Bcc' => $message->getBCCList(),
157
			'Subject' => $message->getSubject(),
158
		];
159
160
		if (!is_null($message->getRepliedMessage())) {
161
			$headers['In-Reply-To'] = $message->getRepliedMessage()->getMessageId();
162
		}
163
164
		$mail = new Horde_Mime_Mail();
165
		$mail->addHeaders($headers);
166
		$mail->setBody($message->getContent());
167
168
		// Append attachments
169
		foreach ($message->getAttachments() as $attachment) {
170
			$mail->addMimePart($attachment);
171
		}
172
173
		// Send the message
174
		$transport = $this->createTransport();
175
		$mail->send($transport);
176
177
		// Save the message in the sent folder
178 4
		$sentFolder = $this->getSentFolder();
179
		/** @var resource $raw */
180
		$raw = stream_get_contents($mail->getRaw());
181
		$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...
182
			Horde_Imap_Client::FLAG_SEEN
183
		]);
184
185
		// Delete draft if one exists
186
		if (!is_null($draftUID)) {
187
			$draftsFolder = $this->getDraftsFolder();
188
			$draftsFolder->setMessageFlag($draftUID, Horde_Imap_Client::FLAG_DELETED, true);
189
			$this->deleteDraft($draftUID);
190
		}
191
	}
192
193
	/**
194
	 * @param IMessage $message
195
	 * @param int|null $previousUID
196
	 * @return int
197
	 */
198
	public function saveDraft(IMessage $message, $previousUID) {
199
		// build mime body
200
		$from = new Horde_Mail_Rfc822_Address($message->getFrom());
201
		$from->personal = $this->getName();
202
		$headers = [
203
			'From' => $from,
204
			'To' => $message->getToList(),
205
			'Cc' => $message->getCCList(),
206
			'Bcc' => $message->getBCCList(),
207
			'Subject' => $message->getSubject(),
208
			'Date' => Horde_Mime_Headers_Date::create(),
209
		];
210
211
		$mail = new Horde_Mime_Mail();
212
		$mail->addHeaders($headers);
213
		$body = new Horde_Mime_Part();
214
		$body->setType('text/plain');
215
		$body->setContents($message->getContent());
216
		$mail->setBasePart($body);
217
218
		// create transport and save message
219
		// save the message in the drafts folder
220
		$draftsFolder = $this->getDraftsFolder();
221
		/** @var resource $raw */
222
		$raw = $mail->getRaw();
223
		$newUid = $draftsFolder->saveDraft(stream_get_contents($raw));
224
225
		// delete old version if one exists
226
		if (!is_null($previousUID)) {
227
			$draftsFolder->setMessageFlag($previousUID, \Horde_Imap_Client::FLAG_DELETED,
228
				true);
229
			$this->deleteDraft($previousUID);
230
		}
231
232
		return $newUid;
233
	}
234
235
	/**
236
	 * @param string $mailBox
237
	 */
238 12
	public function deleteMailbox($mailBox) {
239 12
		if ($mailBox instanceof Mailbox) {
240
			$mailBox = $mailBox->getFolderId();
241
		}
242 12
		$conn = $this->getImapConnection();
243 12
		$conn->deleteMailbox($mailBox);
244 1
		$this->mailboxes = null;
245 1
	}
246
247
	/**
248
	 * Lists mailboxes (folders) for this account.
249
	 *
250
	 * Lists mailboxes and also queries the server for their 'special use',
251
	 * eg. inbox, sent, trash, etc
252
	 *
253
	 * @param string $pattern Pattern to match mailboxes against. All by default.
254
	 * @return Mailbox[]
255
	 */
256 4
	protected function listMailboxes($pattern = '*') {
257
		// open the imap connection
258 4
		$conn = $this->getImapConnection();
259
260
		// if successful -> get all folders of that account
261 4
		$mailBoxes = $conn->listMailboxes($pattern, Horde_Imap_Client::MBOX_ALL,
262
			[
263 4
			'delimiter' => true,
264 4
			'attributes' => true,
265 4
			'special_use' => true,
266
			'sort' => true
267 4
		]);
268
269 4
		$mailboxes = [];
270 4
		foreach ($mailBoxes as $mailbox) {
271 4
			$mailboxes[] = new Mailbox($conn, $mailbox['mailbox'],
272 4
				$mailbox['attributes'], $mailbox['delimiter']);
273 4
			if ($mailbox['mailbox']->utf8 === 'INBOX') {
274 4
				$mailboxes[] = new SearchMailbox($conn, $mailbox['mailbox'],
275 4
					$mailbox['attributes'], $mailbox['delimiter']);
276 4
			}
277 4
		}
278
279 4
		return $mailboxes;
280
	}
281
282
	/**
283
	 * @param string $folderId
284
	 * @return \OCA\Mail\Mailbox
285
	 */
286 12
	public function getMailbox($folderId) {
287 12
		if ($this->mailbox === null) {
288 1
			$conn = $this->getImapConnection();
289 1
			$parts = explode('/', $folderId);
290 1
			if (count($parts) > 1 && $parts[1] === 'FLAGGED') {
291
				$mailbox = new Horde_Imap_Client_Mailbox($parts[0]);
292
				return new SearchMailbox($conn, $mailbox, []);
293
			}
294 1
			$mailbox = new Horde_Imap_Client_Mailbox($folderId);
295 1
			$this->mailbox = new Mailbox($conn, $mailbox, []);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \OCA\Mail\Mailbox($conn, $mailbox, array()) of type object<OCA\Mail\Mailbox> is incompatible with the declared type object<Horde_Imap_Client_Mailbox> of property $mailbox.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
296 1
		}
297
298 12
		return $this->mailbox;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->mailbox; of type OCA\Mail\Mailbox|Horde_Imap_Client_Mailbox adds the type Horde_Imap_Client_Mailbox to the return on line 298 which is incompatible with the return type declared by the interface OCA\Mail\Service\IAccount::getMailbox of type OCA\Mail\Service\IMailBox.
Loading history...
299
	}
300
301
	/**
302
	 * Get a list of all mailboxes in this account
303
	 *
304
	 * @return Mailbox[]
305
	 */
306 4
	public function getMailboxes() {
307 4
		if ($this->mailboxes === null) {
308 4
			$this->mailboxes = $this->listMailboxes();
309 4
			$this->sortMailboxes();
310 4
			$this->localizeSpecialMailboxes();
311 4
		}
312
313 4
		return $this->mailboxes;
314
	}
315
316
	/**
317
	 * @return array
318
	 */
319 3
	public function getListArray() {
320
321 3
		$folders = [];
322 3
		$mailBoxes = $this->getMailboxes();
323
		$mailBoxNames = array_map(function($mb) {
324
			/** @var Mailbox $mb */
325 3
			return $mb->getFolderId();
326
		}, array_filter($mailBoxes, function($mb) {
327
			/** @var Mailbox $mb */
328 3
			return (!$mb instanceof SearchMailbox) && (!in_array('\noselect', $mb->attributes()));
329 3
		}));
330
331 3
		$status = $this->getImapConnection()->status($mailBoxNames);
332 3
		foreach ($mailBoxes as $mailbox) {
333 3
			$s = isset($status[$mailbox->getFolderId()]) ? $status[$mailbox->getFolderId()] : null;
334 3
			$folders[] = $mailbox->getListArray($this->getId(), $s);
335 3
		}
336 3
		$delimiter = reset($folders)['delimiter'];
337
		return [
338 3
			'id' => $this->getId(),
339 3
			'email' => $this->getEMailAddress(),
340 3
			'folders' => array_values($folders),
341 3
			'specialFolders' => $this->getSpecialFoldersIds(),
342 3
			'delimiter' => $delimiter,
343 3
		];
344
	}
345
346
	/**
347
	 * @return Horde_Mail_Transport
348
	 */
349
	public function createTransport() {
350
		$transport = $this->config->getSystemValue('app.mail.transport', 'smtp');
351
		if ($transport === 'php-mail') {
352
			return new Horde_Mail_Transport_Mail();
353
		}
354
355
		$password = $this->account->getOutboundPassword();
356
		$password = $this->crypto->decrypt($password);
357
		$params = [
358
			'host' => $this->account->getOutboundHost(),
359
			'password' => $password,
360
			'port' => $this->account->getOutboundPort(),
361
			'username' => $this->account->getOutboundUser(),
362
			'secure' => $this->convertSslMode($this->account->getOutboundSslMode()),
363
			'timeout' => 2
364
		];
365
		return new Horde_Mail_Transport_Smtphorde($params);
366
	}
367
368
	/**
369
	 * Lists special use folders for this account.
370
	 *
371
	 * The special uses returned are the "best" one for each special role,
372
	 * picked amongst the ones returned by the server, as well
373
	 * as the one guessed by our code.
374
	 *
375
	 * @param bool $base64_encode
376
	 * @return array In the form [<special use>=><folder id>, ...]
377
	 */
378 4
	public function getSpecialFoldersIds($base64_encode=true) {
379 4
		$folderRoles = ['inbox', 'sent', 'drafts', 'trash', 'archive', 'junk', 'flagged', 'all'];
380 4
		$specialFoldersIds = [];
381
382 4
		foreach ($folderRoles as $role) {
383 4
			$folders = $this->getSpecialFolder($role, true);
384 4
			$specialFoldersIds[$role] = (count($folders) === 0) ? null : $folders[0]->getFolderId();
385 4
			if ($specialFoldersIds[$role] !== null && $base64_encode === true) {
386 3
				$specialFoldersIds[$role] = base64_encode($specialFoldersIds[$role]);
387 3
			}
388 4
		}
389 4
		return $specialFoldersIds;
390
	}
391
392
	/**
393
	 * Get the "drafts" mailbox
394
	 *
395
	 * @return Mailbox The best candidate for the "drafts" inbox
396
	 */
397 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...
398
		// check for existence
399
		$draftsFolder = $this->getSpecialFolder('drafts', true);
400
		if (count($draftsFolder) === 0) {
401
			// drafts folder does not exist - let's create one
402
			$conn = $this->getImapConnection();
403
			// TODO: also search for translated drafts mailboxes
404
			$conn->createMailbox('Drafts', [
405
				'special_use' => ['drafts'],
406
			]);
407
			return $this->guessBestMailBox($this->listMailboxes('Drafts'));
408
		}
409
		return $draftsFolder[0];
410
	}
411
412
	/**
413
	 * @return IMailBox
414
	 */
415
	public function getInbox() {
416
		$folders = $this->getSpecialFolder('inbox', false);
417
		return count($folders) > 0 ? $folders[0] : null;
418
	}
419
420
	/**
421
	 * Get the "sent mail" mailbox
422
	 *
423
	 * @return Mailbox The best candidate for the "sent mail" inbox
424
	 */
425 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...
426
		//check for existence
427
		$sentFolders = $this->getSpecialFolder('sent', true);
428
		if (count($sentFolders) === 0) {
429
			//sent folder does not exist - let's create one
430
			$conn = $this->getImapConnection();
431
			//TODO: also search for translated sent mailboxes
432
			$conn->createMailbox('Sent', [
433
				'special_use' => ['sent'],
434
			]);
435
			return $this->guessBestMailBox($this->listMailboxes('Sent'));
436
		}
437
		return $sentFolders[0];
438
	}
439
440
	/**
441
	 * @param string $sourceFolderId
442
	 * @param int $messageId
443
	 */
444
	public function deleteMessage($sourceFolderId, $messageId) {
445
		$mb = $this->getMailbox($sourceFolderId);
446
		$hordeSourceMailBox = $mb->getHordeMailBox();
447
		// by default we will create a 'Trash' folder if no trash is found
448
		$trashId = "Trash";
449
		$createTrash = true;
450
451
		$trashFolders = $this->getSpecialFolder('trash', true);
452
453
		if (count($trashFolders) !== 0) {
454
			$trashId = $trashFolders[0]->getFolderId();
455
			$createTrash = false;
456
		} else {
457
			// no trash -> guess
458
			$trashes = array_filter($this->getMailboxes(), function($box) {
459
				/**
460
				 * @var Mailbox $box
461
				 */
462
				return (stripos($box->getDisplayName(), 'trash') !== false);
463
			});
464
			if (!empty($trashes)) {
465
				$trashId = array_values($trashes);
466
				$trashId = $trashId[0]->getFolderId();
467
				$createTrash = false;
468
			}
469
		}
470
471
		$hordeMessageIds = new Horde_Imap_Client_Ids($messageId);
472
		$hordeTrashMailBox = new Horde_Imap_Client_Mailbox($trashId);
473
474
		if ($sourceFolderId === $trashId) {
475
			$this->getImapConnection()->expunge($hordeSourceMailBox,
476
				array('ids' => $hordeMessageIds, 'delete' => true));
477
478
			\OC::$server->getLogger()->info("Message expunged: {message} from mailbox {mailbox}",
479
				array('message' => $messageId, 'mailbox' => $sourceFolderId));
480
		} else {
481
			$this->getImapConnection()->copy($hordeSourceMailBox, $hordeTrashMailBox,
482
				array('create' => $createTrash, 'move' => true, 'ids' => $hordeMessageIds));
483
484
			\OC::$server->getLogger()->info("Message moved to trash: {message} from mailbox {mailbox}",
485
				array('message' => $messageId, 'mailbox' => $sourceFolderId, 'app' => 'mail'));
486
		}
487
	}
488
489
	/**
490
	 * 
491
	 * @param int $messageId
492
	 */
493
	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...
494
		$draftsFolder = $this->getDraftsFolder();
495
		
496
		$draftsMailBox = new \Horde_Imap_Client_Mailbox($draftsFolder->getFolderId(), false);
497
		$this->getImapConnection()->expunge($draftsMailBox);
498
	}
499
500
	/**
501
	 * Get 'best' mailbox guess
502
	 *
503
	 * For now the best candidate is the one with
504
	 * the most messages in it.
505
	 *
506
	 * @param array $folders
507
	 * @return Mailbox
508
	 */
509
	protected function guessBestMailBox(array $folders) {
510
		$maxMessages = -1;
511
		$bestGuess = null;
512
		foreach ($folders as $folder) {
513
			/** @var Mailbox $folder */
514
			if ($folder->getTotalMessages() > $maxMessages) {
515
				$maxMessages = $folder->getTotalMessages();
516
				$bestGuess = $folder;
517
			}
518
		}
519
		return $bestGuess;
520
	}
521
522
	/**
523
	 * Get mailbox(es) that have the given special use role
524
	 *
525
	 * With this method we can get a list of all mailboxes that have been
526
	 * determined to have a specific special use role. It can also return
527
	 * the best candidate for this role, for situations where we want
528
	 * one single folder.
529
	 *
530
	 * @param string $role Special role of the folder we want to get ('sent', 'inbox', etc.)
531
	 * @param bool $guessBest If set to true, return only the folder with the most messages in it
532
	 *
533
	 * @return Mailbox[] if $guessBest is false, or Mailbox if $guessBest is true. Empty [] if no match.
534
	 */
535 4
	protected function getSpecialFolder($role, $guessBest=true) {
536
537 4
		$specialFolders = [];
538 4
		foreach ($this->getMailboxes() as $mailbox) {
539 4
			if ($role === $mailbox->getSpecialRole()) {
540 4
				$specialFolders[] = $mailbox;
541 4
			}
542 4
		}
543
544 4
		if ($guessBest === true && count($specialFolders) > 1) {
545
			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...
546
		} else {
547 4
			return $specialFolders;
548
		}
549
	}
550
551
	/**
552
	 *  Localizes the name of the special use folders
553
	 *
554
	 *  The display name of the best candidate folder for each special use
555
	 *  is localized to the user's language
556
	 */
557 4
	protected function localizeSpecialMailboxes() {
558
559 4
		$l = \OC::$server->getL10N('mail');
560
		$map = [
561
			// TRANSLATORS: translated mail box name
562 4
			'inbox'   => $l->t('Inbox'),
563
			// TRANSLATORS: translated mail box name
564 4
			'sent'    => $l->t('Sent'),
565
			// TRANSLATORS: translated mail box name
566 4
			'drafts'  => $l->t('Drafts'),
567
			// TRANSLATORS: translated mail box name
568 4
			'archive' => $l->t('Archive'),
569
			// TRANSLATORS: translated mail box name
570 4
			'trash'   => $l->t('Trash'),
571
			// TRANSLATORS: translated mail box name
572 4
			'junk'    => $l->t('Junk'),
573
			// TRANSLATORS: translated mail box name
574 4
			'all'     => $l->t('All'),
575
			// TRANSLATORS: translated mail box name
576 4
			'flagged' => $l->t('Favorites'),
577 4
		];
578 4
		$mailboxes = $this->getMailboxes();
579 4
		$specialIds = $this->getSpecialFoldersIds(false);
580 4
		foreach ($mailboxes as $i => $mailbox) {
581 4
			if (in_array($mailbox->getFolderId(), $specialIds) === true) {
582 4
				if (isset($map[$mailbox->getSpecialRole()])) {
583 4
					$translatedDisplayName = $map[$mailbox->getSpecialRole()];
584 4
					$mailboxes[$i]->setDisplayName((string)$translatedDisplayName);
585 4
				}
586 4
			}
587 4
		}
588 4
	}
589
590
	/**
591
	 * Sort mailboxes
592
	 *
593
	 * Sort the array of mailboxes with
594
	 *  - special use folders coming first in this order: all, inbox, flagged, drafts, sent, archive, junk, trash
595
	 *  - 'normal' folders coming after that, sorted alphabetically
596
	 */
597 4
	protected function sortMailboxes() {
598
599 4
		$mailboxes = $this->getMailboxes();
600
		usort($mailboxes, function($a, $b) {
601
			/**
602
			 * @var Mailbox $a
603
			 * @var Mailbox $b
604
			 */
605 4
			$roleA = $a->getSpecialRole();
606 4
			$roleB = $b->getSpecialRole();
607
			$specialRolesOrder = [
608 4
				'all'     => 0,
609 4
				'inbox'   => 1,
610 4
				'flagged' => 2,
611 4
				'drafts'  => 3,
612 4
				'sent'    => 4,
613 4
				'archive' => 5,
614 4
				'junk'    => 6,
615 4
				'trash'   => 7,
616 4
			];
617
			// if there is a flag unknown to us, we ignore it for sorting :
618
			// the folder will be sorted by name like any other 'normal' folder
619 4
			if (array_key_exists($roleA, $specialRolesOrder) === false) {
620 4
				$roleA = null;
621 4
			}
622 4
			if (array_key_exists($roleB, $specialRolesOrder) === false) {
623 4
				$roleB = null;
624 4
			}
625
626 4
			if ($roleA === null && $roleB !== null) {
627 4
				return 1;
628 4
			} elseif ($roleA !== null && $roleB === null) {
629 4
				return -1;
630 4
			} elseif ($roleA !== null && $roleB !== null) {
631 4
				if ($roleA === $roleB) {
632
					return strcasecmp($a->getdisplayName(), $b->getDisplayName());
633
				} else {
634 4
					return $specialRolesOrder[$roleA] - $specialRolesOrder[$roleB];
635
				}
636
			}
637
			// we get here if $roleA === null && $roleB === null
638 4
			return strcasecmp($a->getDisplayName(), $b->getDisplayName());
639 4
		});
640
641 4
		$this->mailboxes = $mailboxes;
642 4
	}
643
644
	/**
645
	 * Convert special security mode values into Horde parameters
646
	 *
647
	 * @param string $sslMode
648
	 * @return false|string
649
	 */
650
	protected function convertSslMode($sslMode) {
651
		switch ($sslMode) {
652
			case 'none':
653
				return false;
654
		}
655
		return $sslMode;
656
	}
657
658
	/**
659
	 * @param $query
660
	 * @return array
661
	 */
662 1
	public function getChangedMailboxes($query) {
663 1
		$imp = $this->getImapConnection();
664 1
		$allBoxes = $this->getMailboxes();
665 1
		$allBoxesMap = [];
666 1
		foreach ($allBoxes as $mb) {
667 1
			$allBoxesMap[$mb->getFolderId()] = $mb;
668 1
		}
669
670
		// filter non existing mailboxes
671
		$mailBoxNames = array_filter(array_keys($query), function($folderId) use ($allBoxesMap) {
672 1
			return isset($allBoxesMap[$folderId]);
673 1
		});
674
675 1
		$status = $imp->status($mailBoxNames);
676
677
		// filter for changed mailboxes
678 1
		$changedBoxes = [];
679 1
		foreach ($status as $folderId => $s) {
680
			$uidValidity = $query[$folderId]['uidvalidity'];
681
			$uidNext = $query[$folderId]['uidnext'];
682
683
			if ($uidNext === $s['uidnext'] &&
684
				$uidValidity === $s['uidvalidity']) {
685
				continue;
686
			}
687
			// get unread messages
688
			if (isset($allBoxesMap[$folderId])) {
689
				/** @var Mailbox $m */
690
				$m = $allBoxesMap[$folderId];
691
				$role = $m->getSpecialRole();
692
				if (is_null($role) || $role === 'inbox') {
693
					$newMessages = $m->getMessagesSince($uidNext, $s['uidnext']);
694
					// only trigger updates in case new messages are actually available
695
					if (!empty($newMessages)) {
696
						$changedBoxes[$folderId] = $m->getListArray($this->getId(), $s);
697
						$changedBoxes[$folderId]['messages'] = $newMessages;
698
						$newUnreadMessages = array_filter($newMessages, function($m) {
699
							return $m['flags']['unseen'];
700
						});
701
						$changedBoxes[$folderId]['newUnReadCounter'] = count($newUnreadMessages);
702
					}
703
				}
704
			}
705 1
		}
706
707 1
		return $changedBoxes;
708
	}
709
710
	/**
711
	 * @throws \Horde_Imap_Client_Exception
712
	 */
713
	public function reconnect() {
714
		$this->mailboxes = null;
715
		if ($this->client) {
716
			$this->client->close();
717
			$this->client = null;
718
		}
719
		$this->getImapConnection();
720
	}
721
722
	/**
723
	 * @return array
724
	 */
725
	public function getConfiguration() {
726
		return $this->account->toJson();
727
	}
728
729
	/**
730
	 * @return string|Horde_Mail_Rfc822_List
731
	 */
732
	public function getEmail() {
733
		return $this->account->getEmail();
734
	}
735
736
	public function testConnectivity() {
737
		// connect to imap
738
		$this->getImapConnection();
739
740
		// connect to smtp
741
		$smtp = $this->createTransport();
742
		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...
743
			$smtp->getSMTPObject();
744
		}
745
	}
746
747
	/**
748
	 * Factory method for creating new messages
749
	 *
750
	 * @return OCA\Mail\Model\IMessage
751
	 */
752
	public function newMessage() {
753
		return new Message();
754
	}
755
756
	/**
757
	 * Factory method for creating new reply messages
758
	 *
759
	 * @return OCA\Mail\Model\ReplyMessage
760
	 */
761
	public function newReplyMessage() {
762
		return new ReplyMessage();
763
	}
764
765
}
766