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) { |
|
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
|
|||
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) { |
||
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 |
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:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.