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 | * ownCloud - Contact object |
||
4 | * |
||
5 | * @author Thomas Tanghus |
||
6 | * @copyright 2012-2014 Thomas Tanghus ([email protected]) |
||
7 | * |
||
8 | * This library is free software; you can redistribute it and/or |
||
9 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE |
||
10 | * License as published by the Free Software Foundation; either |
||
11 | * version 3 of the License, or any later version. |
||
12 | * |
||
13 | * This library is distributed in the hope that it will be useful, |
||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
16 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU Affero General Public |
||
19 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
||
20 | * |
||
21 | */ |
||
22 | |||
23 | namespace OCA\Contacts; |
||
24 | |||
25 | use Exception; |
||
26 | use Sabre\VObject\Property; |
||
27 | use OCA\Contacts\Utils\Properties; |
||
28 | |||
29 | /** |
||
30 | * Subclass this class or implement IPIMObject interface for PIM objects |
||
31 | */ |
||
32 | |||
33 | class Contact extends VObject\VCard implements IPIMObject { |
||
34 | |||
35 | /** |
||
36 | * The name of the object type in this case VCARD. |
||
37 | * |
||
38 | * This is used when serializing the object. |
||
39 | * |
||
40 | * @var string |
||
41 | */ |
||
42 | public $name = 'VCARD'; |
||
43 | |||
44 | /** |
||
45 | * @brief language object |
||
46 | * |
||
47 | * @var \OCP\IL10N |
||
48 | */ |
||
49 | public static $l10n; |
||
50 | |||
51 | protected $props = array(); |
||
52 | |||
53 | /** |
||
54 | * Create a new Contact object |
||
55 | * |
||
56 | * @param AddressBook $parent |
||
57 | * @param Backend\AbstractBackend $backend |
||
58 | * @param mixed $data |
||
59 | */ |
||
60 | 7 | public function __construct($parent, $backend, $data = null) { |
|
61 | 7 | parent::__construct('VCARD'); |
|
62 | |||
63 | 7 | self::$l10n = $parent::$l10n; |
|
0 ignored issues
–
show
|
|||
64 | //\OCP\Util::writeLog('contacts', __METHOD__ . ' , data: ' . print_r($data, true), \OCP\Util::DEBUG); |
||
65 | 7 | $this->props['parent'] = $parent; |
|
66 | 7 | $this->props['backend'] = $backend; |
|
67 | 7 | $this->props['retrieved'] = false; |
|
68 | 7 | $this->props['saved'] = false; |
|
69 | |||
70 | 7 | if (!is_null($data)) { |
|
71 | 7 | if ($data instanceof VObject\VCard) { |
|
72 | 3 | View Code Duplication | foreach ($data->children as $child) { |
73 | 3 | if($child->name === 'VERSION' || $child->name === 'PRODID') { |
|
74 | 3 | parent::__set($child->name, $child); |
|
75 | 3 | } else { |
|
76 | 3 | $this->add($child); |
|
77 | } |
||
78 | 3 | } |
|
79 | 3 | $this->setRetrieved(true); |
|
80 | 7 | } elseif (is_array($data)) { |
|
81 | 6 | foreach ($data as $key => $value) { |
|
82 | switch ($key) { |
||
83 | 6 | case 'id': |
|
84 | 6 | $this->props['id'] = $value; |
|
85 | 6 | break; |
|
86 | 6 | case 'permissions': |
|
87 | 6 | $this->props['permissions'] = $value; |
|
88 | 6 | break; |
|
89 | 6 | case 'lastmodified': |
|
90 | 2 | $this->props['lastmodified'] = $value; |
|
91 | 2 | break; |
|
92 | 6 | case 'uri': |
|
93 | 2 | $this->props['uri'] = $value; |
|
94 | 2 | break; |
|
95 | 6 | case 'carddata': |
|
96 | 6 | $this->props['carddata'] = $value; |
|
97 | 6 | $this->retrieve(); |
|
98 | 6 | break; |
|
99 | 6 | case 'vcard': |
|
100 | $this->props['vcard'] = $value; |
||
101 | $this->retrieve(); |
||
102 | break; |
||
103 | 6 | case 'displayname': |
|
104 | 6 | case 'fullname': |
|
105 | 6 | if(is_string($value)) { |
|
106 | 6 | $this->props['displayname'] = $value; |
|
107 | 6 | $this->FN = $value; |
|
108 | // Set it to saved again as we're not actually changing anything |
||
109 | 6 | $this->setSaved(); |
|
110 | 6 | } |
|
111 | 6 | break; |
|
112 | } |
||
113 | 6 | } |
|
114 | 6 | } |
|
115 | 7 | } |
|
116 | 7 | } |
|
117 | |||
118 | /** |
||
119 | * @return array|null |
||
120 | */ |
||
121 | 2 | public function getMetaData() { |
|
122 | 2 | if (!$this->hasPermission(\OCP\Constants::PERMISSION_READ)) { |
|
123 | throw new Exception(self::$l10n->t('You do not have permissions to see this contact'), 403); |
||
124 | } |
||
125 | 2 | if (!isset($this->props['displayname'])) { |
|
126 | if (!$this->retrieve()) { |
||
127 | \OCP\Util::writeLog('contacts', __METHOD__.' error reading: '.print_r($this->props, true), \OCP\Util::ERROR); |
||
128 | return null; |
||
129 | } |
||
130 | } |
||
131 | return array( |
||
132 | 2 | 'id' => $this->getId(), |
|
133 | 2 | 'displayname' => $this->getDisplayName(), |
|
134 | 2 | 'permissions' => $this->getPermissions(), |
|
135 | 2 | 'lastmodified' => $this->lastModified(), |
|
136 | 2 | 'owner' => $this->getOwner(), |
|
137 | 2 | 'parent' => $this->getParent()->getId(), |
|
138 | 2 | 'backend' => $this->getBackend()->name, |
|
139 | 2 | ); |
|
140 | } |
||
141 | |||
142 | /** |
||
143 | * Get a unique key combined of backend name, address book id and contact id. |
||
144 | * |
||
145 | * @return string |
||
146 | */ |
||
147 | public function combinedKey() { |
||
148 | return $this->getBackend()->name . '::' . $this->getParent()->getId() . '::' . $this->getId(); |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * @return string|null |
||
153 | */ |
||
154 | 2 | public function getOwner() { |
|
155 | 2 | return isset($this->props['owner']) |
|
156 | 2 | ? $this->props['owner'] |
|
157 | 2 | : $this->getParent()->getOwner(); |
|
158 | } |
||
159 | |||
160 | /** |
||
161 | * @return string|null |
||
162 | */ |
||
163 | 8 | public function getId() { |
|
164 | 8 | return isset($this->props['id']) ? $this->props['id'] : null; |
|
165 | } |
||
166 | |||
167 | /** |
||
168 | * @return string|null |
||
169 | */ |
||
170 | 5 | public function getDisplayName() { |
|
171 | 5 | if (!$this->hasPermission(\OCP\Constants::PERMISSION_READ)) { |
|
172 | throw new Exception(self::$l10n->t('You do not have permissions to see this contact'), 403); |
||
173 | } |
||
174 | 5 | return isset($this->props['displayname']) |
|
175 | 5 | ? $this->props['displayname'] |
|
176 | 5 | : (isset($this->FN) ? $this->FN : null); |
|
177 | } |
||
178 | |||
179 | /** |
||
180 | * @return string|null |
||
181 | */ |
||
182 | public function getURI() { |
||
183 | return isset($this->props['uri']) ? $this->props['uri'] : null; |
||
184 | } |
||
185 | |||
186 | /** |
||
187 | * @return string |
||
188 | * TODO: Cache result. |
||
189 | */ |
||
190 | public function getETag() { |
||
191 | $this->retrieve(); |
||
192 | return md5($this->serialize()); |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * If this object is part of a collection return a reference |
||
197 | * to the parent object, otherwise return null. |
||
198 | * @return IPIMObject|null |
||
199 | */ |
||
200 | 7 | public function getParent() { |
|
201 | 7 | return $this->props['parent']; |
|
202 | } |
||
203 | |||
204 | 3 | public function getBackend() { |
|
205 | 3 | return $this->props['backend']; |
|
206 | } |
||
207 | |||
208 | /** CRUDS permissions (Create, Read, Update, Delete, Share) |
||
209 | * |
||
210 | * @return integer |
||
211 | */ |
||
212 | 6 | public function getPermissions() { |
|
213 | 6 | return isset($this->props['permissions']) |
|
214 | 6 | ? $this->props['permissions'] |
|
215 | 6 | : $this->getParent()->getPermissions(); |
|
216 | } |
||
217 | |||
218 | /** |
||
219 | * @param integer $permission |
||
220 | * @return integer |
||
221 | */ |
||
222 | 6 | public function hasPermission($permission) { |
|
223 | 6 | return $this->getPermissions() & $permission; |
|
224 | } |
||
225 | |||
226 | /** |
||
227 | * Save the address book data to backend |
||
228 | * FIXME |
||
229 | * |
||
230 | * @param array $data |
||
231 | * @return bool |
||
232 | */ |
||
233 | /* public function update(array $data) { |
||
234 | |||
235 | foreach($data as $key => $value) { |
||
236 | switch($key) { |
||
237 | case 'displayname': |
||
238 | $this->addressBookInfo['displayname'] = $value; |
||
239 | break; |
||
240 | case 'description': |
||
241 | $this->addressBookInfo['description'] = $value; |
||
242 | break; |
||
243 | } |
||
244 | } |
||
245 | return $this->props['backend']->updateContact( |
||
246 | $this->getParent()->getId(), |
||
247 | $this->getId(), |
||
248 | $this |
||
249 | ); |
||
250 | } |
||
251 | */ |
||
252 | /** |
||
253 | * Delete the data from backend |
||
254 | * |
||
255 | * FIXME: Should be removed as it could leave the parent with a dataless object. |
||
256 | * |
||
257 | * @return bool |
||
258 | */ |
||
259 | public function delete() { |
||
260 | if (!$this->hasPermission(\OCP\Constants::PERMISSION_DELETE)) { |
||
261 | throw new Exception(self::$l10n->t('You do not have permissions to delete this contact'), 403); |
||
262 | } |
||
263 | return $this->props['backend']->deleteContact( |
||
264 | $this->getParent()->getId(), |
||
265 | $this->getId() |
||
266 | ); |
||
267 | } |
||
268 | |||
269 | /** |
||
270 | * Save the contact data to backend |
||
271 | * |
||
272 | * @return bool |
||
273 | */ |
||
274 | 3 | public function save($force = false) { |
|
275 | 3 | if (!$this->hasPermission(\OCP\Constants::PERMISSION_UPDATE)) { |
|
276 | throw new Exception(self::$l10n->t('You do not have permissions to update this contact'), 403); |
||
277 | } |
||
278 | 3 | if ($this->isSaved() && !$force) { |
|
279 | \OCP\Util::writeLog('contacts', __METHOD__.' Already saved: ' . print_r($this->props, true), \OCP\Util::DEBUG); |
||
280 | return true; |
||
281 | } |
||
282 | |||
283 | 3 | if (isset($this->FN)) { |
|
284 | 3 | $this->props['displayname'] = (string)$this->FN; |
|
285 | 3 | } |
|
286 | |||
287 | 3 | if ($this->getId()) { |
|
288 | if (!$this->getBackend()->hasContactMethodFor(\OCP\Constants::PERMISSION_UPDATE)) { |
||
289 | throw new Exception(self::$l10n->t('The backend for this contact does not support updating it'), 501); |
||
290 | } |
||
291 | if ($this->getBackend() |
||
292 | ->updateContact( |
||
293 | $this->getParent()->getId(), |
||
294 | $this->getId(), |
||
295 | $this |
||
296 | ) |
||
297 | ) { |
||
298 | $this->props['lastmodified'] = time(); |
||
299 | $this->setSaved(true); |
||
300 | return true; |
||
301 | } else { |
||
302 | return false; |
||
303 | } |
||
304 | } else { |
||
305 | 3 | if (!$this->getBackend()->hasContactMethodFor(\OCP\Constants::PERMISSION_CREATE)) { |
|
306 | throw new Exception(self::$l10n->t('This backend does not support adding contacts'), 501); |
||
307 | } |
||
308 | 3 | $this->props['id'] = $this->getBackend()->createContact( |
|
309 | 3 | $this->getParent()->getId(), $this |
|
310 | 3 | ); |
|
311 | 3 | $this->setSaved(true); |
|
312 | 3 | return $this->getId() !== false; |
|
313 | } |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Get the data from the backend |
||
318 | * FIXME: Clean this up and make sure the logic is OK. |
||
319 | * |
||
320 | * @return bool |
||
321 | */ |
||
322 | 7 | public function retrieve() { |
|
323 | 6 | if ($this->isRetrieved()) { |
|
324 | //\OCP\Util::writeLog('contacts', __METHOD__. ' children', \OCP\Util::DEBUG); |
||
325 | 6 | return true; |
|
326 | } else { |
||
327 | 6 | $data = null; |
|
328 | 6 | if(isset($this->props['vcard']) |
|
329 | 6 | && $this->props['vcard'] instanceof VObject\VCard) { |
|
330 | foreach($this->props['vcard']->children() as $child) { |
||
331 | if($child->name === 'VERSION' || $child->name === 'PRODID') { |
||
332 | parent::__set($child->name, $child); |
||
333 | } else { |
||
334 | $this->add($child); |
||
335 | } |
||
336 | if($child->name === 'FN') { |
||
337 | $this->props['displayname'] |
||
338 | = strtr($child->getValue(), array('\,' => ',', '\;' => ';', '\\\\' => '\\')); |
||
339 | } |
||
340 | } |
||
341 | $this->setRetrieved(true); |
||
342 | $this->setSaved(true); |
||
343 | //$this->children = $this->props['vcard']->children(); |
||
344 | unset($this->props['vcard']); |
||
345 | return true; |
||
346 | 6 | } elseif (!isset($this->props['carddata'])) { |
|
347 | 4 | $result = $this->props['backend']->getContact( |
|
348 | 4 | $this->getParent()->getId(), |
|
349 | 4 | $this->getId() |
|
350 | 4 | ); |
|
351 | 4 | if ($result) { |
|
352 | 4 | if (isset($result['vcard']) |
|
353 | 4 | && $result['vcard'] instanceof VObject\VCard) { |
|
354 | View Code Duplication | foreach ($result['vcard']->children() as $child) { |
|
355 | if($child->name === 'VERSION' || $child->name === 'PRODID') { |
||
356 | parent::__set($child->name, $child); |
||
357 | } else { |
||
358 | $this->add($child); |
||
359 | } |
||
360 | } |
||
361 | $this->setRetrieved(true); |
||
362 | return true; |
||
363 | 4 | } elseif (isset($result['carddata'])) { |
|
364 | // Save internal values |
||
365 | 4 | $data = $result['carddata']; |
|
366 | 4 | $this->props['carddata'] = $result['carddata']; |
|
367 | 4 | $this->props['lastmodified'] = isset($result['lastmodified']) |
|
368 | 4 | ? $result['lastmodified'] |
|
369 | 4 | : null; |
|
370 | 4 | $this->props['displayname'] = $result['displayname']; |
|
371 | 4 | $this->props['permissions'] = $result['permissions']; |
|
372 | 4 | } else { |
|
373 | \OCP\Util::writeLog('contacts', __METHOD__ |
||
374 | . ' Could not get vcard or carddata: ' |
||
375 | . $this->getId() |
||
376 | . print_r($result, true), \OCP\Util::DEBUG); |
||
377 | return false; |
||
378 | } |
||
379 | 4 | } else { |
|
380 | \OCP\Util::writeLog('contacts', __METHOD__.' Error getting contact: ' . $this->getId(), \OCP\Util::DEBUG); |
||
381 | } |
||
382 | 7 | } elseif (isset($this->props['carddata'])) { |
|
383 | 2 | $data = $this->props['carddata']; |
|
384 | 2 | } |
|
385 | try { |
||
386 | 6 | $obj = \Sabre\VObject\Reader::read( |
|
387 | 6 | $data, |
|
388 | \Sabre\VObject\Reader::OPTION_IGNORE_INVALID_LINES |
||
389 | 6 | ); |
|
390 | 6 | if ($obj) { |
|
391 | 6 | View Code Duplication | foreach ($obj->children as $child) { |
392 | 6 | if($child->name === 'VERSION' || $child->name === 'PRODID') { |
|
393 | 6 | parent::__set($child->name, $child); |
|
394 | 6 | } else { |
|
395 | 6 | $this->add($child); |
|
396 | } |
||
397 | 6 | } |
|
398 | 6 | $this->setRetrieved(true); |
|
399 | 6 | $this->setSaved(true); |
|
400 | 6 | } else { |
|
401 | \OCP\Util::writeLog('contacts', __METHOD__.' Error reading: ' . print_r($data, true), \OCP\Util::DEBUG); |
||
402 | return false; |
||
403 | } |
||
404 | 6 | } catch (Exception $e) { |
|
405 | \OCP\Util::writeLog('contacts', __METHOD__ . |
||
406 | ' Error parsing carddata for: ' . $this->getId() . ' ' . $e->getMessage(), |
||
407 | \OCP\Util::ERROR); |
||
408 | return false; |
||
409 | } |
||
410 | } |
||
411 | 6 | return true; |
|
412 | } |
||
413 | |||
414 | /** |
||
415 | * Get the PHOTO or LOGO |
||
416 | * |
||
417 | * @return \OCP\Image|null |
||
418 | */ |
||
419 | public function getPhoto() { |
||
420 | $image = new \OCP\Image(); |
||
421 | |||
422 | if (isset($this->PHOTO)) { |
||
423 | $photo = $this->PHOTO; |
||
424 | } elseif (isset($this->LOGO)) { |
||
425 | $photo = $this->LOGO; |
||
426 | } else { |
||
427 | return null; |
||
428 | } |
||
429 | |||
430 | $photovalue = $photo->getValue(); |
||
431 | |||
432 | if ( $photo instanceof \Sabre\VObject\Property\Uri && substr($photovalue, 0, 5) === 'data:' ) { |
||
433 | $mimeType = substr($photovalue, 5, strpos($photovalue, ',')-5); |
||
434 | if (strpos($mimeType, ';')) { |
||
435 | $mimeType = substr($mimeType,0,strpos($mimeType, ';')); |
||
436 | } |
||
437 | |||
438 | $photovalue = substr($photovalue, strpos($photovalue,',')+1); |
||
439 | |||
440 | if ($image->loadFromBase64($photovalue)) { |
||
441 | return $image; |
||
442 | } |
||
443 | } elseif ($image->loadFromData($photovalue)) { |
||
444 | return $image; |
||
445 | } |
||
446 | |||
447 | return null; |
||
448 | } |
||
449 | |||
450 | /** |
||
451 | * Set the contact photo. |
||
452 | * |
||
453 | * @param \OCP\Image $photo |
||
454 | */ |
||
455 | public function setPhoto(\OCP\Image $photo) { |
||
456 | // For vCard 3.0 the type must be e.g. JPEG or PNG |
||
457 | // For version 4.0 the full mimetype should be used. |
||
458 | // https://tools.ietf.org/html/rfc2426#section-3.1.4 |
||
459 | if (isset($this->PHOTO)) { |
||
460 | $this->remove('PHOTO'); |
||
461 | } |
||
462 | if (strval($this->VERSION) === '4.0') { |
||
463 | $type = $photo->mimeType(); |
||
464 | $this->add('PHOTO', 'data:'.$type.';base64,'.base64_encode($photo->data())); |
||
465 | } else { |
||
466 | $type = explode('/', $photo->mimeType()); |
||
467 | $type = strtoupper(array_pop($type)); |
||
468 | $this->add('PHOTO', $photo->data(), ['ENCODING' => 'b', 'TYPE' => $type]); |
||
469 | } |
||
470 | $this->setSaved(false); |
||
471 | |||
472 | return true; |
||
473 | } |
||
474 | |||
475 | /** |
||
476 | * Get a property index in the contact by the checksum of its serialized value |
||
477 | * |
||
478 | * @param string $checksum An 8 char m5d checksum. |
||
479 | * @return integer Property by reference |
||
480 | * @throws Exception with error code 404 if the property is not found. |
||
481 | */ |
||
482 | public function getPropertyIndexByChecksum($checksum) { |
||
483 | $this->retrieve(); |
||
484 | $idx = 0; |
||
485 | View Code Duplication | foreach ($this->children as $i => &$property) { |
|
486 | if (substr(md5($property->serialize()), 0, 8) == $checksum ) { |
||
487 | return $idx; |
||
488 | } |
||
489 | $idx += 1; |
||
490 | } |
||
491 | throw new Exception(self::$l10n->t('Property not found'), 404); |
||
492 | } |
||
493 | |||
494 | /** |
||
495 | * Get a property by the checksum of its serialized value |
||
496 | * |
||
497 | * @param string $checksum An 8 char m5d checksum. |
||
498 | * @return \Sabre\VObject\Property Property by reference |
||
499 | * @throws Exception with error code 404 if the property is not found. |
||
500 | */ |
||
501 | public function getPropertyByChecksum($checksum) { |
||
502 | $this->retrieve(); |
||
503 | View Code Duplication | foreach ($this->children as $i => &$property) { |
|
504 | if (substr(md5($property->serialize()), 0, 8) == $checksum ) { |
||
505 | return $property; |
||
506 | } |
||
507 | } |
||
508 | throw new Exception(self::$l10n->t('Property not found'), 404); |
||
509 | } |
||
510 | |||
511 | /** |
||
512 | * Delete a property by the checksum of its serialized value |
||
513 | * It is up to the caller to call ->save() |
||
514 | * |
||
515 | * @param string $checksum An 8 char m5d checksum. |
||
516 | * @throws @see getPropertyByChecksum |
||
517 | */ |
||
518 | public function unsetPropertyByChecksum($checksum) { |
||
519 | $idx = $this->getPropertyIndexByChecksum($checksum); |
||
520 | unset($this->children[$idx]); |
||
521 | $this->setSaved(false); |
||
522 | } |
||
523 | |||
524 | /** |
||
525 | * Set a property by the checksum of its serialized value |
||
526 | * It is up to the caller to call ->save() |
||
527 | * |
||
528 | * @param string $checksum An 8 char m5d checksum. |
||
529 | * @param string $name Property name |
||
530 | * @param mixed $value |
||
531 | * @param array $parameters |
||
532 | * @throws @see getPropertyByChecksum |
||
533 | * @return string new checksum |
||
534 | */ |
||
535 | public function setPropertyByChecksum($checksum, $name, $value, $parameters=array()) { |
||
536 | if ($checksum === 'new') { |
||
537 | $property = $this->createProperty($name); |
||
538 | $this->add($property); |
||
539 | } else { |
||
540 | $property = $this->getPropertyByChecksum($checksum); |
||
541 | } |
||
542 | switch ($name) { |
||
543 | case 'EMAIL': |
||
544 | case 'CLOUD': |
||
545 | $value = strtolower($value); |
||
546 | $property->setValue($value); |
||
547 | break; |
||
548 | case 'ADR': |
||
549 | if(is_array($value)) { |
||
550 | $property->setParts($value); |
||
551 | } else { |
||
552 | $property->setValue($value); |
||
553 | } |
||
554 | break; |
||
555 | case 'IMPP': |
||
556 | if (is_null($parameters) || !isset($parameters['X-SERVICE-TYPE'])) { |
||
557 | throw new \InvalidArgumentException(self::$l10n->t(' Missing IM parameter for: ') . $name. ' ' . $value, 412); |
||
558 | } |
||
559 | $serviceType = $parameters['X-SERVICE-TYPE']; |
||
560 | if (is_array($serviceType)) { |
||
561 | $serviceType = $serviceType[0]; |
||
562 | } |
||
563 | $impp = Utils\Properties::getIMOptions($serviceType); |
||
564 | if (is_null($impp)) { |
||
565 | throw new \UnexpectedValueException(self::$l10n->t('Unknown IM: ') . $serviceType, 415); |
||
566 | } |
||
567 | $value = $impp['protocol'] . ':' . $value; |
||
568 | $property->setValue($value); |
||
569 | break; |
||
570 | View Code Duplication | default: |
|
571 | \OCP\Util::writeLog('contacts', __METHOD__.' adding: '.$name. ' ' . $value, \OCP\Util::DEBUG); |
||
572 | $property->setValue($value); |
||
573 | break; |
||
574 | } |
||
575 | $this->setParameters($property, $parameters, true); |
||
576 | $this->setSaved(false); |
||
577 | return substr(md5($property->serialize()), 0, 8); |
||
578 | } |
||
579 | |||
580 | /** |
||
581 | * Set a property by the property name. |
||
582 | * It is up to the caller to call ->save() |
||
583 | * |
||
584 | * @param string $name Property name |
||
585 | * @param mixed $value |
||
586 | * @param array $parameters |
||
587 | * @return bool |
||
588 | 2 | */ |
|
589 | public function setPropertyByName($name, $value, $parameters=array()) { |
||
590 | // TODO: parameters are ignored for now. |
||
591 | switch ($name) { |
||
592 | case 'BDAY': |
||
593 | try { |
||
594 | $date = New \DateTime($value); |
||
595 | } catch(Exception $e) { |
||
596 | \OCP\Util::writeLog('contacts', |
||
597 | __METHOD__.' DateTime exception: ' . $e->getMessage(), |
||
598 | \OCP\Util::ERROR |
||
599 | ); |
||
600 | return false; |
||
601 | } |
||
602 | $value = $date->format('Y-m-d'); |
||
603 | $this->BDAY = $value; |
||
604 | $this->BDAY->add('VALUE', 'DATE'); |
||
605 | //\OCP\Util::writeLog('contacts', __METHOD__.' BDAY: '.$this->BDAY->serialize(), \OCP\Util::DEBUG); |
||
606 | 2 | break; |
|
607 | case 'CATEGORIES': |
||
608 | case 'N': |
||
609 | case 'ORG': |
||
610 | $property = $this->select($name); |
||
611 | 2 | if (count($property) === 0) { |
|
612 | $property = $this->createProperty($name); |
||
613 | $this->add($property); |
||
614 | View Code Duplication | } elseif (count($property) > 1) { |
|
615 | \OCP\Util::writeLog('contacts', |
||
616 | __METHOD__.' more than one property for ' . $name, |
||
617 | \OCP\Util::ERROR |
||
618 | 2 | ); |
|
619 | return false; |
||
620 | } else { |
||
621 | // select returns an array... |
||
622 | $property = array_shift($property); |
||
623 | } |
||
624 | if (($name === 'N') && !is_array($value)) { |
||
625 | $value = explode(';', (string)$value); |
||
626 | } |
||
627 | if (is_array($value)) { |
||
628 | $property->setParts($value); |
||
629 | } else { |
||
630 | $property->setValue($value); |
||
631 | } |
||
632 | break; |
||
633 | View Code Duplication | default: |
|
634 | \OCP\Util::writeLog('contacts', __METHOD__.' adding: '.$name. ' ' . $value, \OCP\Util::DEBUG); |
||
635 | $this->{$name} = $value; |
||
636 | break; |
||
637 | } |
||
638 | $this->setSaved(false); |
||
639 | return true; |
||
640 | } |
||
641 | |||
642 | protected function setParameters($property, $parameters, $reset = false) { |
||
643 | if (!$parameters) { |
||
644 | return; |
||
645 | } |
||
646 | |||
647 | if ($reset) { |
||
648 | $property->parameters = array(); |
||
649 | } |
||
650 | //debug('Setting parameters: ' . print_r($parameters, true)); |
||
651 | foreach ($parameters as $key => $parameter) { |
||
652 | //debug('Adding parameter: ' . $key); |
||
653 | if (is_array($parameter)) { |
||
654 | foreach ($parameter as $val) { |
||
655 | if (is_array($val)) { |
||
656 | foreach ($val as $val2) { |
||
657 | if (trim($key) && trim($val2)) { |
||
658 | //debug('Adding parameter: '.$key.'=>'.print_r($val2, true)); |
||
659 | $property->add($key, strip_tags($val2)); |
||
660 | } |
||
661 | } |
||
662 | } else { |
||
663 | if (trim($key) && trim($val)) { |
||
664 | //debug('Adding parameter: '.$key.'=>'.print_r($val, true)); |
||
665 | $property->add($key, strip_tags($val)); |
||
666 | } |
||
667 | } |
||
668 | } |
||
669 | } else { |
||
670 | if (trim($key) && trim($parameter)) { |
||
671 | //debug('Adding parameter: '.$key.'=>'.print_r($parameter, true)); |
||
672 | $property->add($key, strip_tags($parameter)); |
||
673 | } |
||
674 | } |
||
675 | } |
||
676 | } |
||
677 | 2 | ||
678 | 2 | public function lastModified() { |
|
679 | if (!isset($this->props['lastmodified']) && !$this->isRetrieved()) { |
||
680 | $this->retrieve(); |
||
681 | 2 | } |
|
682 | 2 | return isset($this->props['lastmodified']) |
|
683 | 2 | ? $this->props['lastmodified'] |
|
684 | : null; |
||
685 | } |
||
686 | |||
687 | /** |
||
688 | * Merge in data from a multi-dimentional array |
||
689 | * |
||
690 | * NOTE: The data has actually already been merged client side! |
||
691 | * NOTE: The only properties coming from the web client are the ones |
||
692 | * defined in \OCA\Contacts\Utils\Properties::$indexProperties and |
||
693 | * UID is skipped for obvious reasons, and PHOTO is currently not updated. |
||
694 | * The data array has this structure: |
||
695 | * |
||
696 | * array( |
||
697 | * 'EMAIL' => array(array('value' => '[email protected]', 'parameters' = array('TYPE' => array('HOME','VOICE')))) |
||
698 | * ); |
||
699 | * @param array $data |
||
700 | * @return bool |
||
701 | */ |
||
702 | public function mergeFromArray(array $data) { |
||
703 | foreach ($data as $name => $properties) { |
||
704 | if (in_array($name, array('PHOTO', 'UID'))) { |
||
705 | continue; |
||
706 | } |
||
707 | View Code Duplication | if (!is_array($properties)) { |
|
708 | \OCP\Util::writeLog('contacts', __METHOD__.' not an array?: ' .$name. ' '.print_r($properties, true), \OCP\Util::DEBUG); |
||
709 | } |
||
710 | if (in_array($name, Utils\Properties::$multiProperties)) { |
||
711 | unset($this->{$name}); |
||
712 | } |
||
713 | foreach ($properties as $parray) { |
||
714 | \OCP\Util::writeLog('contacts', __METHOD__.' adding: ' .$name. ' '.print_r($parray['value'], true) . ' ' . print_r($parray['parameters'], true), \OCP\Util::DEBUG); |
||
715 | if (in_array($name, Utils\Properties::$multiProperties)) { |
||
716 | // TODO: wrap in try/catch, check return value |
||
717 | $this->setPropertyByChecksum('new', $name, $parray['value'], $parray['parameters']); |
||
718 | } else { |
||
719 | // TODO: Check return value |
||
720 | if (!isset($this->{$name})) { |
||
721 | $this->setPropertyByName($name, $parray['value'], $parray['parameters']); |
||
722 | } |
||
723 | } |
||
724 | } |
||
725 | } |
||
726 | $this->setSaved(false); |
||
727 | return true; |
||
728 | } |
||
729 | 3 | ||
730 | 3 | public function __get($key) { |
|
731 | if (!$this->isRetrieved()) { |
||
732 | $this->retrieve(); |
||
733 | } |
||
734 | 3 | ||
735 | return parent::__get($key); |
||
736 | } |
||
737 | 3 | ||
738 | 3 | public function __isset($key) { |
|
739 | if (!$this->isRetrieved()) { |
||
740 | $this->retrieve(); |
||
741 | } |
||
742 | 3 | ||
743 | return parent::__isset($key); |
||
744 | } |
||
745 | 6 | ||
746 | 6 | public function __set($key, $value) { |
|
747 | 4 | if (!$this->isRetrieved()) { |
|
748 | 4 | $this->retrieve(); |
|
749 | 6 | } |
|
750 | 6 | parent::__set($key, $value); |
|
751 | 6 | if ($key === 'FN') { |
|
752 | 6 | $this->props['displayname'] = $value; |
|
753 | 6 | } |
|
754 | 6 | $this->setSaved(false); |
|
755 | } |
||
756 | |||
757 | public function __unset($key) { |
||
758 | if (!$this->isRetrieved()) { |
||
759 | $this->retrieve(); |
||
760 | } |
||
761 | parent::__unset($key); |
||
762 | if ($key === 'PHOTO') { |
||
763 | Properties::cacheThumbnail( |
||
764 | $this->getBackend()->name, |
||
765 | $this->getParent()->getId(), |
||
766 | $this->getId(), |
||
767 | null, |
||
768 | $this, |
||
769 | array('remove' => true) |
||
770 | ); |
||
771 | } |
||
772 | $this->setSaved(false); |
||
773 | } |
||
774 | |||
775 | /** |
||
776 | * @param boolean $state |
||
777 | 7 | */ |
|
778 | 7 | public function setRetrieved($state) { |
|
779 | 7 | $this->props['retrieved'] = $state; |
|
780 | } |
||
781 | 7 | ||
782 | 7 | public function isRetrieved() { |
|
783 | return $this->props['retrieved']; |
||
784 | } |
||
785 | 7 | ||
786 | 7 | public function setSaved($state = true) { |
|
787 | 7 | $this->props['saved'] = $state; |
|
788 | } |
||
789 | 3 | ||
790 | 3 | public function isSaved() { |
|
791 | return $this->props['saved']; |
||
792 | } |
||
793 | |||
794 | /** |
||
795 | * Generate an event to show in the calendar |
||
796 | * |
||
797 | * @return \Sabre\VObject\Component\VCalendar|null |
||
798 | */ |
||
799 | public function getBirthdayEvent() { |
||
800 | if (!isset($this->BDAY)) { |
||
801 | return null; |
||
802 | } |
||
803 | $birthday = $this->BDAY; |
||
804 | if ((string)$birthday) { |
||
805 | $title = str_replace('{name}', |
||
806 | strtr((string)$this->FN, array('\,' => ',', '\;' => ';')), |
||
807 | App::$l10n->t('{name}\'s Birthday') |
||
808 | ); |
||
809 | try { |
||
810 | $date = new \DateTime($birthday); |
||
811 | } catch(Exception $e) { |
||
812 | return null; |
||
813 | } |
||
814 | $vCal = new \Sabre\VObject\Component\VCalendar(); |
||
815 | $vCal->VERSION = '2.0'; |
||
816 | $vEvent = $vCal->createComponent('VEVENT'); |
||
817 | $vEvent->add('DTSTART'); |
||
818 | $vEvent->DTSTART->setDateTime( |
||
819 | $date |
||
820 | ); |
||
821 | $vEvent->DTSTART['VALUE'] = 'DATE'; |
||
822 | $vEvent->add('DTEND'); |
||
823 | $date->add(new \DateInterval('P1D')); |
||
824 | $vEvent->DTEND->setDateTime( |
||
825 | $date |
||
826 | ); |
||
827 | $vEvent->DTEND['VALUE'] = 'DATE'; |
||
828 | $lm = new \DateTime('@' . $this->lastModified()); |
||
829 | $lm->setTimeZone(new \DateTimeZone('UTC')); |
||
830 | $vEvent->DTSTAMP->setDateTime($lm); |
||
831 | $vEvent->{'UID'} = $this->UID; |
||
832 | $vEvent->{'RRULE'} = 'FREQ=YEARLY'; |
||
833 | $vEvent->{'SUMMARY'} = $title . ' (' . $date->format('Y') . ')'; |
||
834 | $vEvent->{'TRANSP'} = 'TRANSPARENT'; |
||
835 | $appInfo = \OCP\App::getAppInfo('contacts'); |
||
836 | $appVersion = \OCP\App::getAppVersion('contacts'); |
||
837 | $vCal->PRODID = '-//ownCloud//NONSGML '.$appInfo['name'].' '.$appVersion.'//EN'; |
||
838 | $vCal->add($vEvent); |
||
839 | return $vCal; |
||
840 | } |
||
841 | |||
842 | return null; |
||
843 | } |
||
844 | |||
845 | } |
||
846 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..