Completed
Pull Request — master (#1276)
by Thomas
05:17
created

Account::createTransport()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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