Passed
Push — master ( b0ee79...88d067 )
by
unknown
09:54 queued 12s
created

Utils::GetOLUidFromICalUid()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 8
nc 2
nop 1
dl 0
loc 12
rs 10
c 1
b 0
f 0
1
<?php
2
/*
3
 * SPDX-License-Identifier: AGPL-3.0-only
4
 * SPDX-FileCopyrightText: Copyright 2007-2016 Zarafa Deutschland GmbH
5
 * SPDX-FileCopyrightText: Copyright 2020-2022 grommunio GmbH
6
 *
7
 * Several utility functions
8
 */
9
10
class Utils {
11
	/**
12
	 * Prints a variable as string
13
	 * If a boolean is sent, 'true' or 'false' is displayed.
14
	 *
15
	 * @param string $var
16
	 *
17
	 * @return string
18
	 */
19
	public static function PrintAsString($var) {
20
		return ($var) ? (($var === true) ? 'true' : $var) : (($var === false) ? 'false' : (($var === '') ? 'empty' : (($var === null) ? 'null' : $var)));
0 ignored issues
show
introduced by
The condition $var === true is always false.
Loading history...
introduced by
The condition $var === false is always false.
Loading history...
introduced by
The condition $var === null is always false.
Loading history...
21
	}
22
23
	/**
24
	 * Splits a "domain\user" string into two values
25
	 * If the string contains only the user, domain is returned empty.
26
	 *
27
	 * @param string $domainuser
28
	 *
29
	 * @return array index 0: user  1: domain
30
	 */
31
	public static function SplitDomainUser($domainuser) {
32
		$pos = strrpos($domainuser, '\\');
33
		if ($pos === false) {
34
			$user = $domainuser;
35
			$domain = '';
36
		}
37
		else {
38
			$domain = substr($domainuser, 0, $pos);
39
			$user = substr($domainuser, $pos + 1);
40
		}
41
42
		return [$user, $domain];
43
	}
44
45
	/**
46
	 * Build an address string from the components.
47
	 *
48
	 * @param string $street  the street
49
	 * @param string $zip     the zip code
50
	 * @param string $city    the city
51
	 * @param string $state   the state
52
	 * @param string $country the country
53
	 *
54
	 * @return string the address string or null
55
	 */
56
	public static function BuildAddressString($street, $zip, $city, $state, $country) {
57
		$out = "";
58
59
		if (isset($country) && $street != "") {
60
			$out = $country;
61
		}
62
63
		$zcs = "";
64
		if (isset($zip) && $zip != "") {
65
			$zcs = $zip;
66
		}
67
		if (isset($city) && $city != "") {
68
			$zcs .= (($zcs) ? " " : "") . $city;
69
		}
70
		if (isset($state) && $state != "") {
71
			$zcs .= (($zcs) ? " " : "") . $state;
72
		}
73
		if ($zcs) {
74
			$out = $zcs . "\r\n" . $out;
75
		}
76
77
		if (isset($street) && $street != "") {
78
			$out = $street . (($out) ? "\r\n\r\n" . $out : "");
79
		}
80
81
		return ($out) ? $out : null;
82
	}
83
84
	/**
85
	 * Build the fileas string from the components according to the configuration.
86
	 *
87
	 * @param string $lastname
88
	 * @param string $firstname
89
	 * @param string $middlename
90
	 * @param string $company
91
	 *
92
	 * @return string fileas
93
	 */
94
	public static function BuildFileAs($lastname = "", $firstname = "", $middlename = "", $company = "") {
95
		if (defined('FILEAS_ORDER')) {
96
			$fileas = $lastfirst = $firstlast = "";
97
			$names = trim($firstname . " " . $middlename);
98
			$lastname = trim($lastname);
99
			$company = trim($company);
100
101
			// lastfirst is "lastname, firstname middlename"
102
			// firstlast is "firstname middlename lastname"
103
			if (strlen($lastname) > 0) {
104
				$lastfirst = $lastname;
105
				if (strlen($names) > 0) {
106
					$lastfirst .= ", {$names}";
107
					$firstlast = "{$names} {$lastname}";
108
				}
109
				else {
110
					$firstlast = $lastname;
111
				}
112
			}
113
			elseif (strlen($names) > 0) {
114
				$lastfirst = $firstlast = $names;
115
			}
116
117
			// if fileas with a company is selected
118
			// but company is empty then it will
119
			// fallback to firstlast or lastfirst
120
			// (depending on which is selected for company)
121
			switch (FILEAS_ORDER) {
122
				case SYNC_FILEAS_COMPANYONLY:
123
					if (strlen($company) > 0) {
124
						$fileas = $company;
125
					}
126
					elseif (strlen($firstlast) > 0) {
127
						$fileas = $lastfirst;
128
					}
129
					break;
130
131
				case SYNC_FILEAS_COMPANYLAST:
132
					if (strlen($company) > 0) {
133
						$fileas = $company;
134
						if (strlen($lastfirst) > 0) {
135
							$fileas .= "({$lastfirst})";
136
						}
137
					}
138
					elseif (strlen($lastfirst) > 0) {
139
						$fileas = $lastfirst;
140
					}
141
					break;
142
143
				case SYNC_FILEAS_COMPANYFIRST:
144
					if (strlen($company) > 0) {
145
						$fileas = $company;
146
						if (strlen($firstlast) > 0) {
147
							$fileas .= " ({$firstlast})";
148
						}
149
					}
150
					elseif (strlen($firstlast) > 0) {
151
						$fileas = $firstlast;
152
					}
153
					break;
154
155
				case SYNC_FILEAS_FIRSTCOMPANY:
156
					if (strlen($firstlast) > 0) {
157
						$fileas = $firstlast;
158
						if (strlen($company) > 0) {
159
							$fileas .= " ({$company})";
160
						}
161
					}
162
					elseif (strlen($company) > 0) {
163
						$fileas = $company;
164
					}
165
					break;
166
167
				case SYNC_FILEAS_LASTCOMPANY:
168
					if (strlen($lastfirst) > 0) {
169
						$fileas = $lastfirst;
170
						if (strlen($company) > 0) {
171
							$fileas .= " ({$company})";
172
						}
173
					}
174
					elseif (strlen($company) > 0) {
175
						$fileas = $company;
176
					}
177
					break;
178
179
				case SYNC_FILEAS_LASTFIRST:
180
					if (strlen($lastfirst) > 0) {
181
						$fileas = $lastfirst;
182
					}
183
					break;
184
185
				default:
186
					$fileas = $firstlast;
187
					break;
188
			}
189
			if (strlen($fileas) == 0) {
190
				SLog::Write(LOGLEVEL_DEBUG, "Fileas is empty.");
191
			}
192
193
			return $fileas;
194
		}
195
		SLog::Write(LOGLEVEL_DEBUG, "FILEAS_ORDER not defined. Add it to your config.php.");
196
197
		return null;
198
	}
199
200
	/**
201
	 * Checks if the PHP-MAPI extension is available and in a requested version.
202
	 *
203
	 * @param string $version the version to be checked ("6.30.10-18495", parts or build number)
204
	 *
205
	 * @return bool installed version is superior to the checked string
206
	 */
207
	public static function CheckMapiExtVersion($version = "") {
208
		if (!extension_loaded("mapi")) {
209
			return false;
210
		}
211
		// compare build number if requested
212
		if (preg_match('/^\d+$/', $version) && strlen($version) > 3) {
213
			$vs = preg_split('/-/', phpversion("mapi"));
214
215
			return $version <= $vs[1];
216
		}
217
		if (version_compare(phpversion("mapi"), $version) == -1) {
218
			return false;
219
		}
220
221
		return true;
222
	}
223
224
	/**
225
	 * Parses and returns an ecoded vCal-Uid from an OL compatible GlobalObjectID.
226
	 *
227
	 * @param string $olUid an OL compatible GlobalObjectID
228
	 *
229
	 * @return string the vCal-Uid if available in the olUid, else the original olUid as HEX
230
	 */
231
	public static function GetICalUidFromOLUid($olUid) {
232
		// check if "vCal-Uid" is somewhere in outlookid case-insensitive
233
		$icalUid = stristr($olUid, "vCal-Uid");
234
		if ($icalUid !== false) {
235
			// get the length of the ical id - go back 4 position from where "vCal-Uid" was found
236
			$begin = unpack("V", substr($olUid, strlen($icalUid) * (-1) - 4, 4));
237
			// remove "vCal-Uid" and packed "1" and use the ical id length
238
			return substr($icalUid, 12, ($begin[1] - 13));
239
		}
240
241
		return strtoupper(bin2hex($olUid));
242
	}
243
244
	/**
245
	 * Extracts the basedate of the GlobalObjectID and the RecurStartTime.
246
	 *
247
	 * @param string $goid           OL compatible GlobalObjectID
248
	 * @param long   $recurStartTime
0 ignored issues
show
Bug introduced by
The type long was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
249
	 *
250
	 * @return long basedate
251
	 */
252
	public static function ExtractBaseDate($goid, $recurStartTime) {
253
		$hexbase = substr(bin2hex($goid), 32, 8);
254
		$day = hexdec(substr($hexbase, 6, 2));
255
		$month = hexdec(substr($hexbase, 4, 2));
256
		$year = hexdec(substr($hexbase, 0, 4));
257
258
		if ($day && $month && $year) {
259
			$h = $recurStartTime >> 12;
260
			$m = ($recurStartTime - $h * 4096) >> 6;
261
			$s = $recurStartTime - $h * 4096 - $m * 64;
262
263
			return gmmktime($h, $m, $s, $month, $day, $year);
0 ignored issues
show
Bug introduced by
It seems like $day can also be of type double; however, parameter $day of gmmktime() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

263
			return gmmktime($h, $m, $s, $month, /** @scrutinizer ignore-type */ $day, $year);
Loading history...
Bug introduced by
It seems like $month can also be of type double; however, parameter $month of gmmktime() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

263
			return gmmktime($h, $m, $s, /** @scrutinizer ignore-type */ $month, $day, $year);
Loading history...
Bug introduced by
It seems like $year can also be of type double; however, parameter $year of gmmktime() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

263
			return gmmktime($h, $m, $s, $month, $day, /** @scrutinizer ignore-type */ $year);
Loading history...
Bug Best Practice introduced by
The expression return gmmktime($h, $m, $s, $month, $day, $year) returns the type integer which is incompatible with the documented return type long.
Loading history...
264
		}
265
266
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type long.
Loading history...
267
	}
268
269
	/**
270
	 * Converts SYNC_FILTERTYPE into a timestamp.
271
	 *
272
	 * @param int $filtertype Filtertype
273
	 *
274
	 * @return long
275
	 */
276
	public static function GetCutOffDate($filtertype) {
277
		$back = Utils::GetFiltertypeInterval($filtertype);
278
279
		if ($back === false) {
280
			return 0; // unlimited
0 ignored issues
show
Bug Best Practice introduced by
The expression return 0 returns the type integer which is incompatible with the documented return type long.
Loading history...
281
		}
282
283
		return time() - $back;
0 ignored issues
show
Bug Best Practice introduced by
The expression return time() - $back returns the type integer which is incompatible with the documented return type long.
Loading history...
284
	}
285
286
	/**
287
	 * Returns the interval indicated by the filtertype.
288
	 *
289
	 * @param int $filtertype
290
	 *
291
	 * @return bool|long returns false on invalid filtertype
292
	 */
293
	public static function GetFiltertypeInterval($filtertype) {
294
		$back = false;
295
296
		switch ($filtertype) {
297
			case SYNC_FILTERTYPE_1DAY:
298
				$back = 60 * 60 * 24;
299
				break;
300
301
			case SYNC_FILTERTYPE_3DAYS:
302
				$back = 60 * 60 * 24 * 3;
303
				break;
304
305
			case SYNC_FILTERTYPE_1WEEK:
306
				$back = 60 * 60 * 24 * 7;
307
				break;
308
309
			case SYNC_FILTERTYPE_2WEEKS:
310
				$back = 60 * 60 * 24 * 14;
311
				break;
312
313
			case SYNC_FILTERTYPE_1MONTH:
314
				$back = 60 * 60 * 24 * 31;
315
				break;
316
317
			case SYNC_FILTERTYPE_3MONTHS:
318
				$back = 60 * 60 * 24 * 31 * 3;
319
				break;
320
321
			case SYNC_FILTERTYPE_6MONTHS:
322
				$back = 60 * 60 * 24 * 31 * 6;
323
				break;
324
325
			default:
326
				$back = false;
327
		}
328
329
		return $back;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $back also could return the type integer which is incompatible with the documented return type boolean|long.
Loading history...
330
	}
331
332
	/**
333
	 * Converts SYNC_TRUNCATION into bytes.
334
	 *
335
	 * @param int       SYNC_TRUNCATION
0 ignored issues
show
Bug introduced by
The type SYNC_TRUNCATION was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
336
	 * @param mixed $truncation
337
	 *
338
	 * @return long
339
	 */
340
	public static function GetTruncSize($truncation) {
341
		switch ($truncation) {
342
			case SYNC_TRUNCATION_HEADERS:
343
				return 0;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 0 returns the type integer which is incompatible with the documented return type long.
Loading history...
344
345
			case SYNC_TRUNCATION_512B:
346
				return 512;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 512 returns the type integer which is incompatible with the documented return type long.
Loading history...
347
348
			case SYNC_TRUNCATION_1K:
349
				return 1024;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 1024 returns the type integer which is incompatible with the documented return type long.
Loading history...
350
351
			case SYNC_TRUNCATION_2K:
352
				return 2 * 1024;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 2 * 1024 returns the type integer which is incompatible with the documented return type long.
Loading history...
353
354
			case SYNC_TRUNCATION_5K:
355
				return 5 * 1024;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 5 * 1024 returns the type integer which is incompatible with the documented return type long.
Loading history...
356
357
			case SYNC_TRUNCATION_10K:
358
				return 10 * 1024;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 10 * 1024 returns the type integer which is incompatible with the documented return type long.
Loading history...
359
360
			case SYNC_TRUNCATION_20K:
361
				return 20 * 1024;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 20 * 1024 returns the type integer which is incompatible with the documented return type long.
Loading history...
362
363
			case SYNC_TRUNCATION_50K:
364
				return 50 * 1024;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 50 * 1024 returns the type integer which is incompatible with the documented return type long.
Loading history...
365
366
			case SYNC_TRUNCATION_100K:
367
				return 100 * 1024;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 100 * 1024 returns the type integer which is incompatible with the documented return type long.
Loading history...
368
369
			case SYNC_TRUNCATION_ALL:
370
				return 1024 * 1024; // We'll limit to 1MB anyway
0 ignored issues
show
Bug Best Practice introduced by
The expression return 1024 * 1024 returns the type integer which is incompatible with the documented return type long.
Loading history...
371
372
			default:
373
				return 1024; // Default to 1Kb
0 ignored issues
show
Bug Best Practice introduced by
The expression return 1024 returns the type integer which is incompatible with the documented return type long.
Loading history...
374
		}
375
	}
376
377
	/**
378
	 * Truncate an UTF-8 encoded string correctly.
379
	 *
380
	 * If it's not possible to truncate properly, an empty string is returned
381
	 *
382
	 * @param string $string   the string
383
	 * @param string $length   position where string should be cut
384
	 * @param bool   $htmlsafe doesn't cut html tags in half, doesn't ensure correct html - default: false
385
	 *
386
	 * @return string truncated string
387
	 */
388
	public static function Utf8_truncate($string, $length, $htmlsafe = false) {
389
		// make sure length is always an integer
390
		$length = (int) $length;
391
392
		// if the input string is shorter then the trunction, make sure it's valid UTF-8!
393
		if (strlen($string) <= $length) {
394
			$length = strlen($string) - 1;
395
		}
396
397
		// The intent is not to cut HTML tags in half which causes displaying issues (see ZP-1240).
398
		// The used method just tries to cut outside of tags, without checking tag validity and closing tags.
399
		if ($htmlsafe) {
400
			$offset = 0 - strlen($string) + $length;
401
			$validPos = strrpos($string, "<", $offset);
402
			if ($validPos > strrpos($string, ">", $offset)) {
403
				$length = $validPos;
404
			}
405
		}
406
407
		while ($length >= 0) {
408
			if ((ord($string[$length]) < 0x80) || (ord($string[$length]) >= 0xC0)) {
409
				return substr($string, 0, $length);
410
			}
411
			--$length;
412
		}
413
414
		return "";
415
	}
416
417
	/**
418
	 * Indicates if the specified folder type is a system folder.
419
	 *
420
	 * @param int $foldertype
421
	 *
422
	 * @return bool
423
	 */
424
	public static function IsSystemFolder($foldertype) {
425
		return (
426
			$foldertype == SYNC_FOLDER_TYPE_INBOX ||
427
			$foldertype == SYNC_FOLDER_TYPE_DRAFTS ||
428
			$foldertype == SYNC_FOLDER_TYPE_WASTEBASKET ||
429
			$foldertype == SYNC_FOLDER_TYPE_SENTMAIL ||
430
			$foldertype == SYNC_FOLDER_TYPE_OUTBOX ||
431
			$foldertype == SYNC_FOLDER_TYPE_TASK ||
432
			$foldertype == SYNC_FOLDER_TYPE_APPOINTMENT ||
433
			$foldertype == SYNC_FOLDER_TYPE_CONTACT ||
434
			$foldertype == SYNC_FOLDER_TYPE_NOTE ||
435
			$foldertype == SYNC_FOLDER_TYPE_JOURNAL
436
			) ? true : false;
437
	}
438
439
	/**
440
	 * Checks for valid email addresses
441
	 * The used regex actually only checks if a valid email address is part of the submitted string
442
	 * it also returns true for the mailbox format, but this is not checked explicitly.
443
	 *
444
	 * @param string $email address to be checked
445
	 *
446
	 * @return bool
447
	 */
448
	public static function CheckEmail($email) {
449
		return strpos($email, '@') !== false ? true : false;
450
	}
451
452
	/**
453
	 * Checks if a string is base64 encoded.
454
	 *
455
	 * @param string $string the string to be checked
456
	 *
457
	 * @return bool
458
	 */
459
	public static function IsBase64String($string) {
460
		return (bool) preg_match("#^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+/]{4})?$#", $string);
461
	}
462
463
	/**
464
	 * Returns a command string for a given command code.
465
	 *
466
	 * @param int $code
467
	 *
468
	 * @return string or false if code is unknown
469
	 */
470
	public static function GetCommandFromCode($code) {
471
		switch ($code) {
472
			case GSync::COMMAND_SYNC:                 return 'Sync';
473
474
			case GSync::COMMAND_SENDMAIL:             return 'SendMail';
475
476
			case GSync::COMMAND_SMARTFORWARD:         return 'SmartForward';
477
478
			case GSync::COMMAND_SMARTREPLY:           return 'SmartReply';
479
480
			case GSync::COMMAND_GETATTACHMENT:        return 'GetAttachment';
481
482
			case GSync::COMMAND_FOLDERSYNC:           return 'FolderSync';
483
484
			case GSync::COMMAND_FOLDERCREATE:         return 'FolderCreate';
485
486
			case GSync::COMMAND_FOLDERDELETE:         return 'FolderDelete';
487
488
			case GSync::COMMAND_FOLDERUPDATE:         return 'FolderUpdate';
489
490
			case GSync::COMMAND_MOVEITEMS:            return 'MoveItems';
491
492
			case GSync::COMMAND_GETITEMESTIMATE:      return 'GetItemEstimate';
493
494
			case GSync::COMMAND_MEETINGRESPONSE:      return 'MeetingResponse';
495
496
			case GSync::COMMAND_SEARCH:               return 'Search';
497
498
			case GSync::COMMAND_SETTINGS:             return 'Settings';
499
500
			case GSync::COMMAND_PING:                 return 'Ping';
501
502
			case GSync::COMMAND_ITEMOPERATIONS:       return 'ItemOperations';
503
504
			case GSync::COMMAND_PROVISION:            return 'Provision';
505
506
			case GSync::COMMAND_RESOLVERECIPIENTS:    return 'ResolveRecipients';
507
508
			case GSync::COMMAND_VALIDATECERT:         return 'ValidateCert';
509
			// Deprecated commands
510
			case GSync::COMMAND_GETHIERARCHY:         return 'GetHierarchy';
511
512
			case GSync::COMMAND_CREATECOLLECTION:     return 'CreateCollection';
513
514
			case GSync::COMMAND_DELETECOLLECTION:     return 'DeleteCollection';
515
516
			case GSync::COMMAND_MOVECOLLECTION:       return 'MoveCollection';
517
518
			case GSync::COMMAND_NOTIFY:               return 'Notify';
519
		}
520
521
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
522
	}
523
524
	/**
525
	 * Returns a command code for a given command.
526
	 *
527
	 * @param string $command
528
	 *
529
	 * @return int or false if command is unknown
530
	 */
531
	public static function GetCodeFromCommand($command) {
532
		switch ($command) {
533
			case 'Sync':                 return GSync::COMMAND_SYNC;
534
535
			case 'SendMail':             return GSync::COMMAND_SENDMAIL;
536
537
			case 'SmartForward':         return GSync::COMMAND_SMARTFORWARD;
538
539
			case 'SmartReply':           return GSync::COMMAND_SMARTREPLY;
540
541
			case 'GetAttachment':        return GSync::COMMAND_GETATTACHMENT;
542
543
			case 'FolderSync':           return GSync::COMMAND_FOLDERSYNC;
544
545
			case 'FolderCreate':         return GSync::COMMAND_FOLDERCREATE;
546
547
			case 'FolderDelete':         return GSync::COMMAND_FOLDERDELETE;
548
549
			case 'FolderUpdate':         return GSync::COMMAND_FOLDERUPDATE;
550
551
			case 'MoveItems':            return GSync::COMMAND_MOVEITEMS;
552
553
			case 'GetItemEstimate':      return GSync::COMMAND_GETITEMESTIMATE;
554
555
			case 'MeetingResponse':      return GSync::COMMAND_MEETINGRESPONSE;
556
557
			case 'Search':               return GSync::COMMAND_SEARCH;
558
559
			case 'Settings':             return GSync::COMMAND_SETTINGS;
560
561
			case 'Ping':                 return GSync::COMMAND_PING;
562
563
			case 'ItemOperations':       return GSync::COMMAND_ITEMOPERATIONS;
564
565
			case 'Provision':            return GSync::COMMAND_PROVISION;
566
567
			case 'ResolveRecipients':    return GSync::COMMAND_RESOLVERECIPIENTS;
568
569
			case 'ValidateCert':         return GSync::COMMAND_VALIDATECERT;
570
			// Deprecated commands
571
			case 'GetHierarchy':         return GSync::COMMAND_GETHIERARCHY;
572
573
			case 'CreateCollection':     return GSync::COMMAND_CREATECOLLECTION;
574
575
			case 'DeleteCollection':     return GSync::COMMAND_DELETECOLLECTION;
576
577
			case 'MoveCollection':       return GSync::COMMAND_MOVECOLLECTION;
578
579
			case 'Notify':               return GSync::COMMAND_NOTIFY;
580
		}
581
582
		return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
583
	}
584
585
	/**
586
	 * Normalize the given timestamp to the start of the day.
587
	 *
588
	 * @param long $timestamp
589
	 *
590
	 * @return long
591
	 */
592
	public static function getDayStartOfTimestamp($timestamp) {
593
		return $timestamp - ($timestamp % (60 * 60 * 24));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $timestamp - $timestamp % 60 * 60 * 24 returns the type integer which is incompatible with the documented return type long.
Loading history...
594
	}
595
596
	/**
597
	 * Returns a formatted string output from an optional timestamp.
598
	 * If no timestamp is sent, NOW is used.
599
	 *
600
	 * @param long $timestamp
601
	 *
602
	 * @return string
603
	 */
604
	public static function GetFormattedTime($timestamp = false) {
605
		if (!$timestamp) {
606
			return @strftime("%d/%m/%Y %H:%M:%S");
0 ignored issues
show
Bug Best Practice introduced by
The expression return @strftime('%d/%m/%Y %H:%M:%S') could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
607
		}
608
609
		return @strftime("%d/%m/%Y %H:%M:%S", $timestamp);
0 ignored issues
show
Bug introduced by
$timestamp of type long is incompatible with the type integer|null expected by parameter $timestamp of strftime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

609
		return @strftime("%d/%m/%Y %H:%M:%S", /** @scrutinizer ignore-type */ $timestamp);
Loading history...
Bug Best Practice introduced by
The expression return @strftime('%d/%m/%Y %H:%M:%S', $timestamp) could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
610
	}
611
612
	/**
613
	 * Get charset name from a codepage.
614
	 *
615
	 * @see http://msdn.microsoft.com/en-us/library/dd317756(VS.85).aspx
616
	 *
617
	 * Table taken from common/codepage.cpp
618
	 *
619
	 * @param int codepage Codepage
0 ignored issues
show
Bug introduced by
The type codepage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
620
	 * @param mixed $codepage
621
	 *
622
	 * @return string iconv-compatible charset name
623
	 */
624
	public static function GetCodepageCharset($codepage) {
625
		$codepages = [
626
			20106 => "DIN_66003",
627
			20108 => "NS_4551-1",
628
			20107 => "SEN_850200_B",
629
			950 => "big5",
630
			50221 => "csISO2022JP",
631
			51932 => "euc-jp",
632
			51936 => "euc-cn",
633
			51949 => "euc-kr",
634
			949 => "euc-kr",
635
			936 => "gb18030",
636
			52936 => "csgb2312",
637
			852 => "ibm852",
638
			866 => "ibm866",
639
			50220 => "iso-2022-jp",
640
			50222 => "iso-2022-jp",
641
			50225 => "iso-2022-kr",
642
			1252 => "windows-1252",
643
			28591 => "iso-8859-1",
644
			28592 => "iso-8859-2",
645
			28593 => "iso-8859-3",
646
			28594 => "iso-8859-4",
647
			28595 => "iso-8859-5",
648
			28596 => "iso-8859-6",
649
			28597 => "iso-8859-7",
650
			28598 => "iso-8859-8",
651
			28599 => "iso-8859-9",
652
			28603 => "iso-8859-13",
653
			28605 => "iso-8859-15",
654
			20866 => "koi8-r",
655
			21866 => "koi8-u",
656
			932 => "shift-jis",
657
			1200 => "unicode",
658
			1201 => "unicodebig",
659
			65000 => "utf-7",
660
			65001 => "utf-8",
661
			1250 => "windows-1250",
662
			1251 => "windows-1251",
663
			1253 => "windows-1253",
664
			1254 => "windows-1254",
665
			1255 => "windows-1255",
666
			1256 => "windows-1256",
667
			1257 => "windows-1257",
668
			1258 => "windows-1258",
669
			874 => "windows-874",
670
			20127 => "us-ascii",
671
		];
672
673
		if (isset($codepages[$codepage])) {
674
			return $codepages[$codepage];
675
		}
676
		// Defaulting to iso-8859-15 since it is more likely for someone to make a mistake in the codepage
677
		// when using west-european charsets then when using other charsets since utf-8 is binary compatible
678
		// with the bottom 7 bits of west-european
679
		return "iso-8859-15";
680
	}
681
682
	/**
683
	 * Converts a string encoded with codepage into an UTF-8 string.
684
	 *
685
	 * @param int    $codepage
686
	 * @param string $string
687
	 *
688
	 * @return string
689
	 */
690
	public static function ConvertCodepageStringToUtf8($codepage, $string) {
691
		if (function_exists("iconv")) {
692
			$charset = self::GetCodepageCharset($codepage);
693
694
			return iconv($charset, "utf-8", $string);
695
		}
696
697
		SLog::Write(LOGLEVEL_WARN, "Utils::ConvertCodepageStringToUtf8() 'iconv' is not available. Charset conversion skipped.");
698
699
		return $string;
700
	}
701
702
	/**
703
	 * Converts a string to another charset.
704
	 *
705
	 * @param int    $in
706
	 * @param int    $out
707
	 * @param string $string
708
	 *
709
	 * @return string
710
	 */
711
	public static function ConvertCodepage($in, $out, $string) {
712
		// do nothing if both charsets are the same
713
		if ($in == $out) {
714
			return $string;
715
		}
716
717
		if (function_exists("iconv")) {
718
			$inCharset = self::GetCodepageCharset($in);
719
			$outCharset = self::GetCodepageCharset($out);
720
721
			return iconv($inCharset, $outCharset, $string);
722
		}
723
724
		SLog::Write(LOGLEVEL_WARN, "Utils::ConvertCodepage() 'iconv' is not available. Charset conversion skipped.");
725
726
		return $string;
727
	}
728
729
	/**
730
	 * Returns the best match of preferred body preference types.
731
	 *
732
	 * @param array $bpTypes
733
	 *
734
	 * @return int
735
	 */
736
	public static function GetBodyPreferenceBestMatch($bpTypes) {
737
		if ($bpTypes === false) {
0 ignored issues
show
introduced by
The condition $bpTypes === false is always false.
Loading history...
738
			return SYNC_BODYPREFERENCE_PLAIN;
739
		}
740
		// The best choice is RTF, then HTML and then MIME in order to save bandwidth
741
		// because MIME is a complete message including the headers and attachments
742
		if (in_array(SYNC_BODYPREFERENCE_RTF, $bpTypes)) {
743
			return SYNC_BODYPREFERENCE_RTF;
744
		}
745
		if (in_array(SYNC_BODYPREFERENCE_HTML, $bpTypes)) {
746
			return SYNC_BODYPREFERENCE_HTML;
747
		}
748
		if (in_array(SYNC_BODYPREFERENCE_MIME, $bpTypes)) {
749
			return SYNC_BODYPREFERENCE_MIME;
750
		}
751
752
		return SYNC_BODYPREFERENCE_PLAIN;
753
	}
754
755
	/**
756
	 * Checks if a file has the same owner and group as the parent directory.
757
	 * If not, owner and group are fixed (being updated to the owner/group of the directory).
758
	 * If the given file is a special file (i.g., /dev/null, fifo), nothing is changed.
759
	 * Function code contributed by Robert Scheck aka rsc.
760
	 *
761
	 * @param string $file
762
	 *
763
	 * @return bool
764
	 */
765
	public static function FixFileOwner($file) {
766
		if (!function_exists('posix_getuid')) {
767
			SLog::Write(LOGLEVEL_DEBUG, "Utils::FixeFileOwner(): Posix subsystem not available, skipping.");
768
769
			return false;
770
		}
771
		if (posix_getuid() == 0 && is_file($file)) {
772
			$dir = dirname($file);
773
			$perm_dir = stat($dir);
774
			$perm_file = stat($file);
775
776
			if ($perm_file['uid'] == 0 && $perm_dir['uid'] == 0 && $perm_dir['gid'] == 0) {
777
				unlink($file);
778
779
				throw new FatalException("FixFileOwner: {$dir} must be owned by the nginx/apache/php user instead of root for debian based systems and by root:grosync for RHEL-based systems");
780
			}
781
782
			if ($perm_dir['uid'] !== $perm_file['uid'] || $perm_dir['gid'] !== $perm_file['gid']) {
783
				chown($file, $perm_dir['uid']);
784
				chgrp($file, $perm_dir['gid']);
785
				chmod($file, 0664);
786
			}
787
		}
788
789
		return true;
790
	}
791
792
	/**
793
	 * Returns AS-style LastVerbExecuted value from the server value.
794
	 *
795
	 * @param int $verb
796
	 *
797
	 * @return int
798
	 */
799
	public static function GetLastVerbExecuted($verb) {
800
		switch ($verb) {
801
			case NOTEIVERB_REPLYTOSENDER:   return AS_REPLYTOSENDER;
802
803
			case NOTEIVERB_REPLYTOALL:      return AS_REPLYTOALL;
804
805
			case NOTEIVERB_FORWARD:         return AS_FORWARD;
806
		}
807
808
		return 0;
809
	}
810
811
	/**
812
	 * Returns the local part from email address.
813
	 *
814
	 * @param string $email
815
	 *
816
	 * @return string
817
	 */
818
	public static function GetLocalPartFromEmail($email) {
819
		$pos = strpos($email, '@');
820
		if ($pos === false) {
821
			return $email;
822
		}
823
824
		return substr($email, 0, $pos);
825
	}
826
827
	/**
828
	 * Format bytes to a more human readable value.
829
	 *
830
	 * @param int $bytes
831
	 * @param int $precision
832
	 *
833
	 * @return string|void
834
	 */
835
	public static function FormatBytes($bytes, $precision = 2) {
836
		if ($bytes <= 0) {
837
			return '0 B';
838
		}
839
840
		$units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB'];
841
		$base = log($bytes, 1024);
842
		$fBase = floor($base);
843
		$pow = pow(1024, $base - $fBase);
844
845
		return sprintf("%.{$precision}f %s", $pow, $units[$fBase]);
846
	}
847
848
	/**
849
	 * Returns folder origin identifier from its id.
850
	 *
851
	 * @param string $folderid
852
	 *
853
	 * @return bool|string matches values of DeviceManager::FLD_ORIGIN_*
854
	 */
855
	public static function GetFolderOriginFromId($folderid) {
856
		$origin = substr($folderid, 0, 1);
857
858
		switch ($origin) {
859
			case DeviceManager::FLD_ORIGIN_CONFIG:
860
			case DeviceManager::FLD_ORIGIN_GAB:
861
			case DeviceManager::FLD_ORIGIN_SHARED:
862
			case DeviceManager::FLD_ORIGIN_USER:
863
			case DeviceManager::FLD_ORIGIN_IMPERSONATED:
864
				return $origin;
865
		}
866
		SLog::Write(LOGLEVEL_WARN, sprintf("Utils->GetFolderOriginFromId(): Unknown folder origin for folder with id '%s'", $folderid));
867
868
		return false;
869
	}
870
871
	/**
872
	 * Returns folder origin as string from its id.
873
	 *
874
	 * @param string $folderid
875
	 *
876
	 * @return string
877
	 */
878
	public static function GetFolderOriginStringFromId($folderid) {
879
		$origin = substr($folderid, 0, 1);
880
881
		switch ($origin) {
882
			case DeviceManager::FLD_ORIGIN_CONFIG:
883
				return 'configured';
884
885
			case DeviceManager::FLD_ORIGIN_GAB:
886
				return 'GAB';
887
888
			case DeviceManager::FLD_ORIGIN_SHARED:
889
				return 'shared';
890
891
			case DeviceManager::FLD_ORIGIN_USER:
892
				return 'user';
893
894
			case DeviceManager::FLD_ORIGIN_IMPERSONATED:
895
				return 'impersonated';
896
		}
897
		SLog::Write(LOGLEVEL_WARN, sprintf("Utils->GetFolderOriginStringFromId(): Unknown folder origin for folder with id '%s'", $folderid));
898
899
		return 'unknown';
900
	}
901
902
	/**
903
	 * Splits the id into folder id and message id parts. A colon in the $id indicates
904
	 * that the id has folderid:messageid format.
905
	 *
906
	 * @param string $id
907
	 *
908
	 * @return array
909
	 */
910
	public static function SplitMessageId($id) {
911
		if (strpos($id, ':') !== false) {
912
			return explode(':', $id);
913
		}
914
915
		return [null, $id];
916
	}
917
918
	/**
919
	 * Converts a string freebusy type into a numeric status.
920
	 *
921
	 * @param string $fbType
922
	 *
923
	 * @return int
924
	 */
925
	public static function GetFbStatusFromType($fbType) {
926
		switch ($fbType) {
927
			case 'Free':
928
				return fbFree;
929
930
			case 'Tentative':
931
				return fbTentative;
932
933
			case 'Busy':
934
				return fbBusy;
935
936
			case 'OOF':
937
				return fbOutOfOffice;
938
		}
939
		SLog::Write(LOGLEVEL_WARN, sprintf("Utils->GetFbStatusFromType(): Unknown free busy type '%s'", $fbType));
940
941
		return fbNoData;
942
	}
943
}
944
945
// TODO Win1252/UTF8 functions are deprecated and will be removed sometime
946
// if the ICS backend is loaded in CombinedBackend and Zarafa > 7
947
// STORE_SUPPORTS_UNICODE is true and the conversion will not be done
948
// for other backends.
949
function utf8_to_windows1252($string, $option = "", $force_convert = false) {
950
	// if the store supports unicode return the string without converting it
951
	if (!$force_convert && defined('STORE_SUPPORTS_UNICODE') && STORE_SUPPORTS_UNICODE === true) {
952
		return $string;
953
	}
954
955
	if (function_exists("iconv")) {
956
		return @iconv("UTF-8", "Windows-1252" . $option, $string);
957
	}
958
959
	return utf8_decode($string); // no euro support here
960
}
961
962
function windows1252_to_utf8($string, $option = "", $force_convert = false) {
963
	// if the store supports unicode return the string without converting it
964
	if (!$force_convert && defined('STORE_SUPPORTS_UNICODE') && STORE_SUPPORTS_UNICODE === true) {
965
		return $string;
966
	}
967
968
	if (function_exists("iconv")) {
969
		return @iconv("Windows-1252", "UTF-8" . $option, $string);
970
	}
971
972
	return utf8_encode($string); // no euro support here
973
}
974
975
function w2u($string) {
976
	return windows1252_to_utf8($string);
977
}
978
function u2w($string) {
979
	return utf8_to_windows1252($string);
980
}
981
982
function w2ui($string) {
983
	return windows1252_to_utf8($string, "//TRANSLIT");
984
}
985
function u2wi($string) {
986
	return utf8_to_windows1252($string, "//TRANSLIT");
987
}
988