This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
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 Christoph Wurst <[email protected]> |
||
5 | * @author Clement Wong <[email protected]> |
||
6 | * @author Jan-Christoph Borchardt <[email protected]> |
||
7 | * @author Lukas Reschke <[email protected]> |
||
8 | * @author matiasdelellis <[email protected]> |
||
9 | * @author Robin McCorkell <[email protected]> |
||
10 | * @author Thomas Imbreckx <[email protected]> |
||
11 | * @author Thomas I <[email protected]> |
||
12 | * @author Thomas Mueller <[email protected]> |
||
13 | * @author Thomas Müller <[email protected]> |
||
14 | * |
||
15 | |||
16 | * |
||
17 | * This code is free software: you can redistribute it and/or modify |
||
18 | * it under the terms of the GNU Affero General Public License, version 3, |
||
19 | * as published by the Free Software Foundation. |
||
20 | * |
||
21 | * This program is distributed in the hope that it will be useful, |
||
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
24 | * GNU Affero General Public License for more details. |
||
25 | * |
||
26 | * You should have received a copy of the GNU Affero General Public License, version 3, |
||
27 | * along with this program. If not, see <http://www.gnu.org/licenses/> |
||
28 | * |
||
29 | */ |
||
30 | |||
31 | namespace OCA\Mail; |
||
32 | |||
33 | use Horde_Imap_Client; |
||
34 | use Horde_Imap_Client_Exception; |
||
35 | use Horde_Imap_Client_Fetch_Query; |
||
36 | use Horde_Imap_Client_Ids; |
||
37 | use Horde_Imap_Client_Mailbox; |
||
38 | use Horde_Imap_Client_Search_Query; |
||
39 | use Horde_Imap_Client_Socket; |
||
40 | use OCA\Mail\Model\IMAPMessage; |
||
41 | use OCA\Mail\Service\IMailBox; |
||
42 | |||
43 | class Mailbox implements IMailBox { |
||
44 | |||
45 | /** |
||
46 | * @var Horde_Imap_Client_Socket |
||
47 | */ |
||
48 | protected $conn; |
||
49 | |||
50 | /** |
||
51 | * @var array |
||
52 | */ |
||
53 | private $attributes; |
||
54 | |||
55 | /** |
||
56 | * @var string |
||
57 | */ |
||
58 | private $specialRole; |
||
59 | |||
60 | /** |
||
61 | * @var string |
||
62 | */ |
||
63 | private $displayName; |
||
64 | |||
65 | /** |
||
66 | * @var string |
||
67 | */ |
||
68 | private $delimiter; |
||
69 | |||
70 | /** |
||
71 | * @var Horde_Imap_Client_Mailbox |
||
72 | */ |
||
73 | protected $mailBox; |
||
74 | |||
75 | /** |
||
76 | * @param Horde_Imap_Client_Socket $conn |
||
77 | * @param Horde_Imap_Client_Mailbox $mailBox |
||
78 | * @param array $attributes |
||
79 | * @param string $delimiter |
||
80 | */ |
||
81 | 12 | public function __construct($conn, $mailBox, $attributes, $delimiter='/') { |
|
82 | 12 | $this->conn = $conn; |
|
83 | 12 | $this->mailBox = $mailBox; |
|
84 | 12 | $this->attributes = $attributes; |
|
85 | 12 | $this->delimiter = $delimiter; |
|
86 | 12 | $this->getSpecialRoleFromAttributes(); |
|
87 | 12 | if ($this->specialRole === null) { |
|
88 | 12 | $this->guessSpecialRole(); |
|
89 | 12 | } |
|
90 | 12 | $this->makeDisplayName(); |
|
91 | 12 | } |
|
92 | |||
93 | 6 | private function getSearchIds($from, $count, $filter) { |
|
94 | 6 | if ($filter instanceof Horde_Imap_Client_Search_Query) { |
|
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
95 | 3 | $query = $filter; |
|
96 | 3 | } else { |
|
97 | 3 | $query = new Horde_Imap_Client_Search_Query(); |
|
98 | 3 | if ($filter) { |
|
99 | $query->text($filter, false); |
||
100 | } |
||
101 | } |
||
102 | 6 | if ($this->getSpecialRole() !== 'trash') { |
|
103 | 6 | $query->flag(Horde_Imap_Client::FLAG_DELETED, false); |
|
104 | 6 | } |
|
105 | 6 | $result = $this->conn->search($this->mailBox, $query, ['sort' => [Horde_Imap_Client::SORT_DATE]]); |
|
106 | 6 | $ids = array_reverse($result['match']->ids); |
|
107 | 6 | if ($from >= 0 && $count >= 0) { |
|
108 | 3 | $ids = array_slice($ids, $from, $count); |
|
109 | 3 | } |
|
110 | 6 | return new \Horde_Imap_Client_Ids($ids, false); |
|
111 | } |
||
112 | |||
113 | private function getFetchIds($from, $count) { |
||
114 | $q = new Horde_Imap_Client_Fetch_Query(); |
||
115 | $q->uid(); |
||
116 | $q->imapDate(); |
||
117 | |||
118 | $result = $this->conn->fetch($this->mailBox, $q); |
||
119 | $uidMap = []; |
||
120 | foreach ($result as $r) { |
||
121 | $uidMap[$r->getUid()] = $r->getImapDate()->getTimeStamp(); |
||
122 | } |
||
123 | // sort by time |
||
124 | uasort($uidMap, function($a, $b) { |
||
125 | return $a < $b; |
||
126 | }); |
||
127 | if ($from >= 0 && $count >= 0) { |
||
128 | $uidMap = array_slice($uidMap, $from, $count, true); |
||
129 | } |
||
130 | return new \Horde_Imap_Client_Ids(array_keys($uidMap), false); |
||
131 | } |
||
132 | |||
133 | 6 | public function getMessages($from = 0, $count = 2, $filter = '') { |
|
134 | 6 | if (!$this->conn->capability->query('SORT') && (is_null($filter) || $filter === '')) { |
|
135 | $ids = $this->getFetchIds($from, $count); |
||
136 | } else { |
||
137 | 6 | $ids = $this->getSearchIds($from, $count, $filter); |
|
138 | } |
||
139 | |||
140 | 6 | $headers = []; |
|
141 | |||
142 | 6 | $fetch_query = new Horde_Imap_Client_Fetch_Query(); |
|
143 | 6 | $fetch_query->envelope(); |
|
144 | 6 | $fetch_query->flags(); |
|
145 | 6 | $fetch_query->size(); |
|
146 | 6 | $fetch_query->uid(); |
|
147 | 6 | $fetch_query->imapDate(); |
|
148 | 6 | $fetch_query->structure(); |
|
149 | |||
150 | 6 | $headers = array_merge($headers, [ |
|
151 | 6 | 'importance', |
|
152 | 6 | 'list-post', |
|
153 | 'x-priority' |
||
154 | 6 | ]); |
|
155 | 6 | $headers[] = 'content-type'; |
|
156 | |||
157 | 6 | $fetch_query->headers('imp', $headers, [ |
|
158 | 6 | 'cache' => true, |
|
159 | 'peek' => true |
||
160 | 6 | ]); |
|
161 | |||
162 | 6 | $options = ['ids' => $ids]; |
|
163 | // $list is an array of Horde_Imap_Client_Data_Fetch objects. |
||
164 | 6 | $headers = $this->conn->fetch($this->mailBox, $fetch_query, $options); |
|
165 | |||
166 | 6 | ob_start(); // fix for Horde warnings |
|
167 | 6 | $messages = []; |
|
168 | 6 | foreach ($headers->ids() as $message_id) { |
|
169 | 6 | $header = $headers[$message_id]; |
|
170 | 6 | $message = new IMAPMessage($this->conn, $this->mailBox, $message_id, $header); |
|
171 | 6 | $messages[] = $message->getListArray(); |
|
172 | 6 | } |
|
173 | 6 | ob_get_clean(); |
|
174 | |||
175 | // sort by time |
||
176 | usort($messages, function($a, $b) { |
||
177 | return $a['dateInt'] < $b['dateInt']; |
||
178 | 6 | }); |
|
179 | |||
180 | 6 | return $messages; |
|
181 | } |
||
182 | |||
183 | /** |
||
184 | * @return array |
||
185 | */ |
||
186 | 3 | public function attributes() { |
|
187 | 3 | return $this->attributes; |
|
188 | } |
||
189 | |||
190 | /** |
||
191 | * @param string $messageId |
||
192 | * @param bool $loadHtmlMessageBody |
||
193 | * @return IMAPMessage |
||
194 | */ |
||
195 | public function getMessage($messageId, $loadHtmlMessageBody = false) { |
||
196 | return new IMAPMessage($this->conn, $this->mailBox, $messageId, null, $loadHtmlMessageBody); |
||
0 ignored issues
–
show
The return type of
return new \OCA\Mail\Mod... $loadHtmlMessageBody); (OCA\Mail\Model\IMAPMessage ) is incompatible with the return type declared by the interface OCA\Mail\Service\IMailBox::getMessage of type OCA\Mail\Message .
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 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
197 | } |
||
198 | |||
199 | /** |
||
200 | * @param int $flags |
||
201 | * @return array |
||
202 | */ |
||
203 | 12 | public function getStatus($flags = \Horde_Imap_Client::STATUS_ALL) { |
|
204 | 12 | return $this->conn->status($this->mailBox, $flags); |
|
205 | } |
||
206 | |||
207 | /** |
||
208 | * @return int |
||
209 | */ |
||
210 | 3 | public function getTotalMessages() { |
|
211 | 3 | $status = $this->getStatus(\Horde_Imap_Client::STATUS_MESSAGES); |
|
212 | 3 | return (int) $status['messages']; |
|
213 | } |
||
214 | |||
215 | 12 | protected function makeDisplayName() { |
|
216 | 12 | $parts = explode($this->delimiter, $this->mailBox->utf8, 2); |
|
217 | |||
218 | 12 | if (count($parts) > 1) { |
|
219 | $displayName = $parts[1]; |
||
220 | 12 | } elseif (strtolower($this->mailBox->utf8) === 'inbox') { |
|
221 | 6 | $displayName = 'Inbox'; |
|
222 | 6 | } else { |
|
223 | 12 | $displayName = $this->mailBox->utf8; |
|
224 | } |
||
225 | |||
226 | 12 | $this->displayName = $displayName; |
|
227 | 12 | } |
|
228 | |||
229 | 13 | public function getFolderId() { |
|
230 | 13 | return $this->mailBox->utf8; |
|
231 | } |
||
232 | |||
233 | /** |
||
234 | * @return string |
||
235 | */ |
||
236 | 6 | public function getParent() { |
|
237 | 6 | $folderId = $this->getFolderId(); |
|
238 | 6 | $parts = explode($this->delimiter, $folderId, 2); |
|
239 | |||
240 | 6 | if (count($parts) > 1) { |
|
241 | return $parts[0]; |
||
242 | } |
||
243 | |||
244 | 6 | return null; |
|
245 | } |
||
246 | |||
247 | /** |
||
248 | * @return string |
||
249 | */ |
||
250 | 9 | public function getSpecialRole() { |
|
251 | 9 | return $this->specialRole; |
|
252 | } |
||
253 | |||
254 | /** |
||
255 | * @return string |
||
256 | */ |
||
257 | 6 | public function getDisplayName() { |
|
258 | 6 | return $this->displayName; |
|
259 | } |
||
260 | |||
261 | /** |
||
262 | * @param string $displayName |
||
263 | */ |
||
264 | 6 | public function setDisplayName($displayName) { |
|
265 | 6 | $this->displayName = $displayName; |
|
266 | 6 | } |
|
267 | |||
268 | /** |
||
269 | * @param integer $accountId |
||
270 | * @return array |
||
271 | */ |
||
272 | 6 | public function getListArray($accountId, $status = null) { |
|
273 | 6 | $displayName = $this->getDisplayName(); |
|
274 | try { |
||
275 | 6 | if (is_null($status)) { |
|
276 | 3 | $status = $this->getStatus(); |
|
277 | 3 | } |
|
278 | 6 | $total = $status['messages']; |
|
279 | 6 | $specialRole = $this->getSpecialRole(); |
|
280 | 6 | $unseen = ($specialRole === 'trash') ? 0 : $status['unseen']; |
|
281 | 6 | $isEmpty = ($total === 0); |
|
282 | 6 | $noSelect = in_array('\\noselect', $this->attributes); |
|
283 | 6 | $parentId = $this->getParent(); |
|
284 | 6 | $parentId = ($parentId !== null) ? base64_encode($parentId) : null; |
|
285 | return [ |
||
286 | 6 | 'id' => base64_encode($this->getFolderId()), |
|
287 | 6 | 'parent' => $parentId, |
|
288 | 6 | 'name' => $displayName, |
|
289 | 6 | 'specialRole' => $specialRole, |
|
290 | 6 | 'unseen' => $unseen, |
|
291 | 6 | 'total' => $total, |
|
292 | 6 | 'isEmpty' => $isEmpty, |
|
293 | 6 | 'accountId' => $accountId, |
|
294 | 6 | 'noSelect' => $noSelect, |
|
295 | 6 | 'uidvalidity' => $status['uidvalidity'], |
|
296 | 6 | 'uidnext' => $status['uidnext'], |
|
297 | 6 | 'delimiter' => $this->delimiter |
|
298 | 6 | ]; |
|
299 | } catch (Horde_Imap_Client_Exception $e) { |
||
0 ignored issues
–
show
|
|||
300 | return [ |
||
301 | 'id' => base64_encode($this->getFolderId()), |
||
302 | 'parent' => null, |
||
303 | 'name' => $displayName, |
||
304 | 'specialRole' => null, |
||
305 | 'unseen' => 0, |
||
306 | 'total' => 0, |
||
307 | 'error' => $e->getMessage(), |
||
308 | 'isEmpty' => true, |
||
309 | 'accountId' => $accountId, |
||
310 | 'noSelect' => true |
||
311 | ]; |
||
312 | } |
||
313 | } |
||
314 | /** |
||
315 | * Get the special use role of the mailbox |
||
316 | * |
||
317 | * This method reads the attributes sent by the server |
||
318 | * |
||
319 | */ |
||
320 | 12 | protected function getSpecialRoleFromAttributes() { |
|
321 | /* |
||
322 | * @todo: support multiple attributes on same folder |
||
323 | * "any given server or message store may support |
||
324 | * any combination of the attributes" |
||
325 | * https://tools.ietf.org/html/rfc6154 |
||
326 | */ |
||
327 | 12 | $result = null; |
|
328 | 12 | if (is_array($this->attributes)) { |
|
329 | /* Convert attributes to lowercase, because gmail |
||
330 | * returns them as lowercase (eg. \trash and not \Trash) |
||
331 | */ |
||
332 | $specialUseAttributes = [ |
||
333 | 12 | strtolower(Horde_Imap_Client::SPECIALUSE_ALL), |
|
334 | 12 | strtolower(Horde_Imap_Client::SPECIALUSE_ARCHIVE), |
|
335 | 12 | strtolower(Horde_Imap_Client::SPECIALUSE_DRAFTS), |
|
336 | 12 | strtolower(Horde_Imap_Client::SPECIALUSE_FLAGGED), |
|
337 | 12 | strtolower(Horde_Imap_Client::SPECIALUSE_JUNK), |
|
338 | 12 | strtolower(Horde_Imap_Client::SPECIALUSE_SENT), |
|
339 | 12 | strtolower(Horde_Imap_Client::SPECIALUSE_TRASH) |
|
340 | 12 | ]; |
|
341 | |||
342 | 12 | $attributes = array_map(function($n) { |
|
343 | 6 | return strtolower($n); |
|
344 | 12 | }, $this->attributes); |
|
345 | |||
346 | 12 | foreach ($specialUseAttributes as $attr) { |
|
347 | 12 | if (in_array($attr, $attributes)) { |
|
348 | 6 | $result = ltrim($attr, '\\'); |
|
349 | 6 | break; |
|
350 | } |
||
351 | 12 | } |
|
352 | |||
353 | 12 | } |
|
354 | |||
355 | 12 | $this->specialRole = $result; |
|
356 | 12 | } |
|
357 | |||
358 | /** |
||
359 | * Assign a special role to this mailbox based on its name |
||
360 | */ |
||
361 | 12 | protected function guessSpecialRole() { |
|
362 | |||
363 | $specialFoldersDict = [ |
||
364 | 12 | 'inbox' => ['inbox'], |
|
365 | 12 | 'sent' => ['sent', 'sent items', 'sent messages', 'sent-mail', 'sentmail'], |
|
366 | 12 | 'drafts' => ['draft', 'drafts'], |
|
367 | 12 | 'archive' => ['archive', 'archives'], |
|
368 | 12 | 'trash' => ['deleted messages', 'trash'], |
|
369 | 12 | 'junk' => ['junk', 'spam', 'bulk mail'], |
|
370 | 12 | ]; |
|
371 | |||
372 | 12 | $lowercaseExplode = explode($this->delimiter, $this->getFolderId(), 2); |
|
373 | 12 | $lowercaseId = strtolower(array_pop($lowercaseExplode)); |
|
374 | 12 | $result = null; |
|
375 | 12 | foreach ($specialFoldersDict as $specialRole => $specialNames) { |
|
376 | 12 | if (in_array($lowercaseId, $specialNames)) { |
|
377 | 6 | $result = $specialRole; |
|
378 | 6 | break; |
|
379 | } |
||
380 | 12 | } |
|
381 | |||
382 | 12 | $this->specialRole = $result; |
|
383 | 12 | } |
|
384 | |||
385 | /** |
||
386 | * @param int $messageId |
||
387 | * @param string $attachmentId |
||
388 | * @return Attachment |
||
389 | */ |
||
390 | public function getAttachment($messageId, $attachmentId) { |
||
391 | return new Attachment($this->conn, $this->mailBox, $messageId, $attachmentId); |
||
392 | } |
||
393 | |||
394 | /** |
||
395 | * @param string $rawBody |
||
396 | * @param array $flags |
||
397 | */ |
||
398 | 6 | public function saveMessage($rawBody, $flags = []) { |
|
399 | |||
400 | 6 | $this->conn->append($this->mailBox, [ |
|
401 | [ |
||
402 | 6 | 'data' => $rawBody, |
|
403 | 'flags' => $flags |
||
404 | 6 | ] |
|
405 | 6 | ]); |
|
406 | 6 | } |
|
407 | |||
408 | /** |
||
409 | * Save draft |
||
410 | * |
||
411 | * @param string $rawBody |
||
412 | * @return int UID of the saved draft |
||
413 | */ |
||
414 | public function saveDraft($rawBody) { |
||
415 | |||
416 | $uids = $this->conn->append($this->mailBox, [ |
||
417 | [ |
||
418 | 'data' => $rawBody, |
||
419 | 'flags' => [ |
||
420 | Horde_Imap_Client::FLAG_DRAFT, |
||
421 | Horde_Imap_Client::FLAG_SEEN |
||
422 | ] |
||
423 | ] |
||
424 | ]); |
||
425 | return $uids->current(); |
||
426 | } |
||
427 | |||
428 | /** |
||
429 | * @param int $uid |
||
430 | * @param string $flag |
||
431 | * @param boolean $add |
||
432 | */ |
||
433 | public function setMessageFlag($uid, $flag, $add) { |
||
434 | $options = [ |
||
435 | 'ids' => new Horde_Imap_Client_Ids($uid) |
||
436 | ]; |
||
437 | if ($add) { |
||
438 | $options['add'] = [$flag]; |
||
439 | } else { |
||
440 | $options['remove'] = [$flag]; |
||
441 | } |
||
442 | $this->conn->store($this->mailBox, $options); |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * @param $fromUid |
||
447 | * @param $toUid |
||
448 | * @return array |
||
449 | */ |
||
450 | 3 | public function getMessagesSince($fromUid, $toUid) { |
|
451 | 3 | $query = new Horde_Imap_Client_Search_Query(); |
|
452 | 3 | $query->ids(new Horde_Imap_Client_Ids("$fromUid:$toUid")); |
|
453 | 3 | return $this->getMessages(-1, -1, $query); |
|
454 | } |
||
455 | |||
456 | /** |
||
457 | * @return Horde_Imap_Client_Mailbox |
||
458 | */ |
||
459 | public function getHordeMailBox() { |
||
460 | return $this->mailBox; |
||
461 | } |
||
462 | |||
463 | } |
||
464 |