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