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 - VCard component |
||
4 | * |
||
5 | * This component represents the BEGIN:VCARD and END:VCARD found in every |
||
6 | * vcard. |
||
7 | * |
||
8 | * @author Thomas Tanghus |
||
9 | * @author Evert Pot (http://www.rooftopsolutions.nl/) |
||
10 | * @copyright 2013-2014 Thomas Tanghus ([email protected]) |
||
11 | * |
||
12 | * This library is free software; you can redistribute it and/or |
||
13 | * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE |
||
14 | * License as published by the Free Software Foundation; either |
||
15 | * version 3 of the License, or any later version. |
||
16 | * |
||
17 | * This library is distributed in the hope that it will be useful, |
||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
20 | * GNU AFFERO GENERAL PUBLIC LICENSE for more details. |
||
21 | * |
||
22 | * You should have received a copy of the GNU Affero General Public |
||
23 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
||
24 | * |
||
25 | */ |
||
26 | |||
27 | namespace OCA\Contacts\VObject; |
||
28 | |||
29 | use OCA\Contacts\Utils; |
||
30 | use Sabre\VObject; |
||
31 | |||
32 | /** |
||
33 | * This class overrides \Sabre\VObject\Component\VCard::validate() to be add |
||
34 | * to import partially invalid vCards by ignoring invalid lines and to |
||
35 | * validate and upgrade using .... |
||
36 | * |
||
37 | * Satisfy PHP Analyzer: |
||
38 | * @property \OC\VObject\CompoundProperty N |
||
39 | * @property \OC\VObject\CompoundProperty ORG |
||
40 | * @property \OC\VObject\CompoundProperty ADR |
||
41 | * @property \OCA\Contacts\VObject\GroupProperty CATEGORIES |
||
42 | * @property \Sabre\VObject\Property\Text FN |
||
43 | * @property \Sabre\VObject\Property\Text EMAIL |
||
44 | * @property \Sabre\VObject\Property\Text VERSION |
||
45 | * @property \Sabre\VObject\Property\Text BDAY |
||
46 | * @property \Sabre\VObject\Property\Text UID |
||
47 | * @property \Sabre\VObject\Property\Text REV |
||
48 | * @property \Sabre\VObject\Property\Binary PHOTO |
||
49 | * @property \Sabre\VObject\Property\Binary LOGO |
||
50 | * @property \Sabre\VObject\Property\FlatText PRODID |
||
51 | */ |
||
52 | class VCard extends VObject\Component\VCard { |
||
53 | |||
54 | /** |
||
55 | * The following constants are used by the validate() method. |
||
56 | */ |
||
57 | const REPAIR = 1; |
||
0 ignored issues
–
show
Coding Style
introduced
by
![]() |
|||
58 | const UPGRADE = 2; |
||
59 | |||
60 | /** |
||
61 | * The groups in the contained properties |
||
62 | * |
||
63 | * @var array |
||
64 | */ |
||
65 | protected $groups = array(); |
||
66 | |||
67 | /** |
||
68 | * VCards with version 2.1, 3.0 and 4.0 are found. |
||
69 | * |
||
70 | * If the VCARD doesn't know its version, 3.0 is assumed and if |
||
71 | * option UPGRADE is given it will be upgraded to version 3.0. |
||
72 | */ |
||
73 | const DEFAULT_VERSION = '3.0'; |
||
74 | |||
75 | /** |
||
76 | * The vCard 2.1 specification allows parameter values without a name. |
||
77 | * The parameter name is then determined from the unique parameter value. |
||
78 | * In version 2.1 e.g. a phone can be formatted like: TEL;HOME;CELL:123456789 |
||
79 | * This has to be changed to either TEL;TYPE=HOME,CELL:123456789 or TEL;TYPE=HOME;TYPE=CELL:123456789 - both are valid. |
||
80 | * |
||
81 | * From: https://github.com/barnabywalters/vcard/blob/master/barnabywalters/VCard/VCard.php |
||
82 | * |
||
83 | * @param string value |
||
84 | * @return string |
||
85 | */ |
||
86 | protected function paramName($value) { |
||
87 | static $types = array ( |
||
88 | 'DOM', 'INTL', 'POSTAL', 'PARCEL','HOME', 'WORK', |
||
89 | 'PREF', 'VOICE', 'FAX', 'MSG', 'CELL', 'PAGER', |
||
90 | 'BBS', 'MODEM', 'CAR', 'ISDN', 'VIDEO', |
||
91 | 'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD', |
||
92 | 'INTERNET', 'IBMMAIL', 'MCIMAIL', |
||
93 | 'POWERSHARE', 'PRODIGY', 'TLX', 'X400', |
||
94 | 'GIF', 'CGM', 'WMF', 'BMP', 'MET', 'PMB', 'DIB', |
||
95 | 'PICT', 'TIFF', 'PDF', 'PS', 'JPEG', 'QTIME', |
||
96 | 'MPEG', 'MPEG2', 'AVI', |
||
97 | 'WAVE', 'AIFF', 'PCM', |
||
98 | 'X509', 'PGP'); |
||
99 | static $values = array ( |
||
100 | 'INLINE', 'URL', 'CID'); |
||
101 | static $encodings = array ( |
||
102 | '7BIT', 'QUOTED-PRINTABLE', 'BASE64'); |
||
103 | $name = 'UNKNOWN'; |
||
104 | if (in_array($value, $types)) { |
||
105 | $name = 'TYPE'; |
||
106 | } elseif (in_array($value, $values)) { |
||
107 | $name = 'VALUE'; |
||
108 | } elseif (in_array($value, $encodings)) { |
||
109 | $name = 'ENCODING'; |
||
110 | } |
||
111 | return $name; |
||
112 | } |
||
113 | |||
114 | /** |
||
115 | * Decode properties for upgrading from v. 2.1 |
||
116 | * |
||
117 | * @param \Sabre\VObject\Property $property Reference to a \Sabre\VObject\Property. |
||
118 | * The only encoding allowed in version 3.0 is 'b' for binary. All encoded strings |
||
119 | * must therefore be decoded and the parameters removed. |
||
120 | */ |
||
121 | protected function decodeProperty(&$property) { |
||
122 | foreach($property->parameters as $key=>&$parameter) { |
||
123 | // Check for values without names which Sabre interprets |
||
124 | // as names without values. |
||
125 | if(trim($parameter->getValue()) === '') { |
||
126 | $parameter->setValue($parameter->name); |
||
127 | $parameter->name = $this->paramName($parameter->name); |
||
128 | } |
||
129 | // Check out for encoded string and decode them :-[ |
||
130 | if(strtoupper($parameter->name) == 'ENCODING') { |
||
131 | if(strtoupper($parameter->getValue()) == 'QUOTED-PRINTABLE') { |
||
132 | $property->setValue(str_replace( |
||
133 | "\r\n", "\n", |
||
134 | VObject\StringUtil::convertToUTF8( |
||
135 | quoted_printable_decode($property->getValue()) |
||
0 ignored issues
–
show
The method
getValue() does not seem to exist on object<Sabre\VObject\Property> .
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||
136 | ) |
||
137 | )); |
||
138 | unset($property->parameters[$key]); |
||
139 | } elseif(strtoupper($parameter->getValue()) == 'BASE64') { |
||
140 | $parameter->setValue('b'); |
||
141 | } |
||
142 | } elseif(strtoupper($parameter->name) == 'CHARSET') { |
||
143 | unset($property->parameters[$key]); |
||
144 | } |
||
145 | } |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * Work around issue in older VObject sersions |
||
150 | * https://github.com/fruux/sabre-vobject/issues/24 |
||
151 | * |
||
152 | * @param \Sabre\VObject\Property $property Reference to a \Sabre\VObject\Property. |
||
153 | */ |
||
154 | public function fixPropertyParameters(&$property) { |
||
155 | // Work around issue in older VObject sersions |
||
156 | // https://github.com/fruux/sabre-vobject/issues/24 |
||
157 | foreach($property->parameters as $key=>$parameter) { |
||
158 | if(strpos($parameter->getValue(), ',') === false) { |
||
159 | continue; |
||
160 | } |
||
161 | $values = explode(',', $parameter->getValue()); |
||
162 | $values = array_map('trim', $values); |
||
163 | $parameter->setValue(array_shift($values)); |
||
164 | foreach($values as $value) { |
||
165 | $property->add($parameter->name, $value); |
||
166 | } |
||
167 | } |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Validates the node for correctness. |
||
172 | * |
||
173 | * The following options are supported: |
||
174 | * - VCard::REPAIR - If something is broken, and automatic repair may |
||
175 | * be attempted. |
||
176 | * - VCard::UPGRADE - If needed the vCard will be upgraded to version 3.0. |
||
177 | * |
||
178 | * An array is returned with warnings. |
||
179 | * |
||
180 | * Every item in the array has the following properties: |
||
181 | * * level - (number between 1 and 3 with severity information) |
||
182 | * * message - (human readable message) |
||
183 | * * node - (reference to the offending node) |
||
184 | * |
||
185 | * @param int $options |
||
186 | * @return array |
||
187 | */ |
||
188 | 4 | public function validate($options = 0) { |
|
189 | |||
190 | 4 | $warnings = array(); |
|
191 | 4 | $repaired = false; |
|
192 | |||
193 | 4 | $version = $this->select('VERSION'); |
|
194 | 4 | if (count($version) !== 1) { |
|
195 | $warnings[] = array( |
||
196 | 'level' => 1, |
||
197 | 'message' => 'The VERSION property must appear in the VCARD component exactly 1 time', |
||
198 | 'node' => $this, |
||
199 | ); |
||
200 | if ($options & self::REPAIR) { |
||
201 | if (count($version) > 1) { |
||
202 | $version = (string) current($version); |
||
203 | $this->remove('VERSION'); |
||
0 ignored issues
–
show
The method
remove() does not exist on OCA\Contacts\VObject\VCard . Did you maybe mean removeFromGroup() ?
This check marks calls to methods that do not seem to exist on an object. This is most likely the result of a method being renamed without all references to it being renamed likewise. ![]() |
|||
204 | $this->VERSION = $version; |
||
205 | } else { |
||
206 | $this->VERSION = self::DEFAULT_VERSION; |
||
207 | } |
||
208 | $repaired = true; |
||
209 | } |
||
210 | } |
||
211 | |||
212 | 4 | $version = (string)$this->VERSION; |
|
213 | 4 | if ($version!=='2.1' && $version!=='3.0' && $version!=='4.0') { |
|
214 | $warnings[] = array( |
||
215 | 'level' => 1, |
||
216 | 1 | 'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.', |
|
217 | 'node' => $this, |
||
218 | ); |
||
219 | 4 | if ($options & self::REPAIR) { |
|
220 | $this->VERSION = self::DEFAULT_VERSION; |
||
221 | $repaired = true; |
||
222 | } |
||
223 | } |
||
224 | |||
225 | # upgrade 2.1 cards to 3.0 cards. |
||
226 | 4 | if ($options & self::UPGRADE && $version === '2.1') { |
|
227 | $this->VERSION = self::DEFAULT_VERSION; |
||
228 | $repaired = true; |
||
229 | foreach($this->children as $idx => &$property) { |
||
230 | |||
231 | $this->decodeProperty($property); |
||
232 | $this->fixPropertyParameters($property); |
||
233 | /* What exactly was I thinking here? |
||
234 | switch((string)$property->name) { |
||
235 | case 'LOGO': |
||
236 | case 'SOUND': |
||
237 | case 'PHOTO': |
||
238 | if(isset($property['TYPE']) && strpos((string)$property['TYPE'], '/') === false) { |
||
239 | $property['TYPE'] = 'image/' . strtolower($property['TYPE']); |
||
240 | } |
||
241 | }*/ |
||
242 | } |
||
243 | } |
||
244 | |||
245 | 4 | $fn = $this->select('FN'); |
|
246 | 4 | if (count($fn) !== 1 || trim((string)$this->FN) === '') { |
|
247 | $warnings[] = array( |
||
248 | 'level' => 1, |
||
249 | 'message' => 'The FN property must appear in the VCARD component exactly 1 time', |
||
250 | 'node' => $this, |
||
251 | ); |
||
252 | if ($options & self::REPAIR) { |
||
253 | // We're going to try to see if we can use the contents of the |
||
254 | // N property. |
||
255 | $repaired = true; |
||
256 | if (isset($this->N) |
||
257 | && substr((string)$this->N, 2) !== ';;' |
||
258 | && (string)$this->N !== '' |
||
259 | ) { |
||
260 | $value = explode(';', (string)$this->N); |
||
261 | if (isset($value[1]) && $value[1]) { |
||
262 | $this->FN = $value[1] . ' ' . $value[0]; |
||
263 | } else { |
||
264 | $this->FN = $value[0]; |
||
265 | } |
||
266 | // Otherwise, the ORG property may work |
||
267 | } elseif (isset($this->ORG)) { |
||
268 | $this->FN = (string)$this->ORG; |
||
269 | } elseif (isset($this->EMAIL)) { |
||
270 | $this->FN = (string)$this->EMAIL; |
||
271 | } |
||
272 | |||
273 | } |
||
274 | } |
||
275 | |||
276 | 4 | if(isset($this->BDAY)) { |
|
277 | if ($options & self::REPAIR) { |
||
278 | // If the BDAY has a format of e.g. 19960401 |
||
279 | $bday = (string)$this->BDAY; |
||
280 | if(strlen($bday) >= 8 |
||
281 | && is_int(substr($bday, 0, 4)) |
||
282 | && is_int(substr($bday, 4, 2)) |
||
283 | && is_int(substr($bday, 6, 2))) { |
||
284 | $this->BDAY = substr($bday, 0, 4).'-'.substr($bday, 4, 2).'-'.substr($bday, 6, 2); |
||
285 | $this->BDAY->VALUE = 'DATE'; |
||
286 | $repaired = true; |
||
287 | } else if($bday[5] !== '-' || $bday[7] !== '-') { |
||
288 | try { |
||
289 | // Skype exports as e.g. Jan 14, 1996 |
||
290 | $date = new \DateTime($bday); |
||
291 | $this->BDAY = $date->format('Y-m-d'); |
||
292 | $this->BDAY->VALUE = 'DATE'; |
||
293 | $repaired = true; |
||
294 | } catch(\Exception $e) { |
||
295 | \OCP\Util::writeLog('contacts', __METHOD__.' Removing invalid BDAY: ' . $bday, \OCP\Util::DEBUG); |
||
296 | unset($this->BDAY); |
||
297 | } |
||
298 | } |
||
299 | } |
||
300 | } |
||
301 | |||
302 | 4 | $n = $this->select('N'); |
|
303 | 4 | if (count($n) !== 1) { |
|
304 | 4 | $warnings[] = array( |
|
305 | 4 | 'level' => 1, |
|
306 | 4 | 'message' => 'The N property must appear in the VCARD component exactly 1 time', |
|
307 | 4 | 'node' => $this, |
|
308 | ); |
||
309 | // TODO: Make a better effort parsing FN. |
||
310 | 4 | if (($options & self::REPAIR) && count($n) === 0) { |
|
311 | // Take 2 first name parts of 'FN' and reverse. |
||
312 | 4 | $slice = array_reverse(array_slice(explode(' ', (string)$this->FN), 0, 2)); |
|
313 | 4 | if(count($slice) < 2) { // If not enought, add one more... |
|
314 | $slice[] = ""; |
||
315 | } |
||
316 | 4 | $this->N = $slice; |
|
317 | 4 | $repaired = true; |
|
318 | 4 | } |
|
319 | 4 | } |
|
320 | |||
321 | 4 | if (!isset($this->UID) || trim((string)$this->UID) === '') { |
|
322 | 1 | $warnings[] = array( |
|
323 | 1 | 'level' => 1, |
|
324 | 1 | 'message' => 'Every vCard must have a UID', |
|
325 | 1 | 'node' => $this, |
|
326 | ); |
||
327 | 1 | if ($options & self::REPAIR) { |
|
328 | 1 | $this->UID = Utils\Properties::generateUID(); |
|
329 | 1 | $repaired = true; |
|
330 | 1 | } |
|
331 | 1 | } |
|
332 | |||
333 | 4 | if ($repaired) { |
|
334 | 4 | $now = new \DateTime; |
|
335 | 4 | $this->REV = $now->format(\DateTime::W3C); |
|
336 | |||
337 | 4 | if (count($this->select('PRODID')) > 1) { |
|
338 | $this->remove('PRODID'); |
||
0 ignored issues
–
show
The method
remove() does not exist on OCA\Contacts\VObject\VCard . Did you maybe mean removeFromGroup() ?
This check marks calls to methods that do not seem to exist on an object. This is most likely the result of a method being renamed without all references to it being renamed likewise. ![]() |
|||
339 | } |
||
340 | 4 | $appInfo = \OCP\App::getAppInfo('contacts'); |
|
341 | 4 | $appVersion = \OCP\App::getAppVersion('contacts'); |
|
342 | 4 | $this->PRODID = '-//ownCloud//NONSGML '.$appInfo['name'].' '.$appVersion.'//EN'; |
|
343 | 4 | } |
|
344 | |||
345 | 4 | return array_merge( |
|
346 | 4 | parent::validate($options), |
|
347 | $warnings |
||
348 | 4 | ); |
|
349 | |||
350 | } |
||
351 | |||
352 | /** |
||
353 | * Get all group names in the vCards properties |
||
354 | * |
||
355 | * NOTE: Not to confuse with CATEGORIES groups |
||
356 | * |
||
357 | * @return array |
||
358 | */ |
||
359 | public function propertyGroups() { |
||
360 | foreach($this->children as $property) { |
||
361 | if($property->group && !isset($this->groups[$property->group])) { |
||
362 | $this->groups[] = $property->group; |
||
363 | } |
||
364 | } |
||
365 | if(count($this->groups) > 1) { |
||
366 | sort($this->groups); |
||
367 | } |
||
368 | return $this->groups; |
||
369 | } |
||
370 | |||
371 | /** |
||
372 | * Test if vcard has group (CATEGORIES) $name |
||
373 | * |
||
374 | * @param string $name |
||
375 | * @return bool |
||
376 | */ |
||
377 | public function inGroup($name) { |
||
378 | if(!isset($this->CATEGORIES)) { |
||
379 | return false; |
||
380 | } |
||
381 | |||
382 | return $this->CATEGORIES->hasGroup($name); |
||
383 | } |
||
384 | |||
385 | /** |
||
386 | * Add group (CATEGORIES) $name to vcard |
||
387 | * |
||
388 | * Return true if contact wasn't already in group |
||
389 | * |
||
390 | * @param string $name |
||
391 | * @return bool |
||
392 | */ |
||
393 | public function addToGroup($name) { |
||
394 | if(!isset($this->CATEGORIES)) { |
||
395 | $this->add('CATEGORIES'); |
||
396 | } |
||
397 | |||
398 | return $this->CATEGORIES->addGroup($name); |
||
399 | } |
||
400 | |||
401 | /** |
||
402 | * Remove group (CATEGORIES) $name from vcard |
||
403 | * |
||
404 | * Return true if vcard has been updated. |
||
405 | * |
||
406 | * @param string $name |
||
407 | * @return bool |
||
408 | */ |
||
409 | public function removeFromGroup($name) { |
||
410 | |||
411 | if(!isset($this->CATEGORIES)) { |
||
412 | return false; |
||
413 | } |
||
414 | |||
415 | $updated = $this->CATEGORIES->removeGroup($name); |
||
416 | // getParts() returns an array with an empty element if |
||
417 | // CATEGORIES is empty |
||
418 | $groups = $this->CATEGORIES->getParts(); |
||
419 | // Remove empty elements |
||
420 | $groups = array_filter($groups, 'strlen'); |
||
421 | if(count($groups) === 0) { |
||
422 | unset($this->{'CATEGORIES'}); |
||
423 | $updated = true; |
||
424 | } |
||
425 | |||
426 | return $updated; |
||
427 | } |
||
428 | |||
429 | |||
430 | } |
||
431 |