Passed
Push — master ( 6fe2c8...c1b1c9 )
by
unknown
08:44 queued 13s
created

Utils::GetLocalPartFromEmail()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 2
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 10
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
	 * Returns AS-style LastVerbExecuted value from the server value.
757
	 *
758
	 * @param int $verb
759
	 *
760
	 * @return int
761
	 */
762
	public static function GetLastVerbExecuted($verb) {
763
		switch ($verb) {
764
			case NOTEIVERB_REPLYTOSENDER:   return AS_REPLYTOSENDER;
765
766
			case NOTEIVERB_REPLYTOALL:      return AS_REPLYTOALL;
767
768
			case NOTEIVERB_FORWARD:         return AS_FORWARD;
769
		}
770
771
		return 0;
772
	}
773
774
	/**
775
	 * Returns the local part from email address.
776
	 *
777
	 * @param string $email
778
	 *
779
	 * @return string
780
	 */
781
	public static function GetLocalPartFromEmail($email) {
782
		$pos = strpos($email, '@');
783
		if ($pos === false) {
784
			return $email;
785
		}
786
787
		return substr($email, 0, $pos);
788
	}
789
790
	/**
791
	 * Format bytes to a more human readable value.
792
	 *
793
	 * @param int $bytes
794
	 * @param int $precision
795
	 *
796
	 * @return string|void
797
	 */
798
	public static function FormatBytes($bytes, $precision = 2) {
799
		if ($bytes <= 0) {
800
			return '0 B';
801
		}
802
803
		$units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB'];
804
		$base = log($bytes, 1024);
805
		$fBase = floor($base);
806
		$pow = pow(1024, $base - $fBase);
807
808
		return sprintf("%.{$precision}f %s", $pow, $units[$fBase]);
809
	}
810
811
	/**
812
	 * Returns folder origin identifier from its id.
813
	 *
814
	 * @param string $folderid
815
	 *
816
	 * @return bool|string matches values of DeviceManager::FLD_ORIGIN_*
817
	 */
818
	public static function GetFolderOriginFromId($folderid) {
819
		$origin = substr($folderid, 0, 1);
820
821
		switch ($origin) {
822
			case DeviceManager::FLD_ORIGIN_CONFIG:
823
			case DeviceManager::FLD_ORIGIN_GAB:
824
			case DeviceManager::FLD_ORIGIN_SHARED:
825
			case DeviceManager::FLD_ORIGIN_USER:
826
			case DeviceManager::FLD_ORIGIN_IMPERSONATED:
827
				return $origin;
828
		}
829
		SLog::Write(LOGLEVEL_WARN, sprintf("Utils->GetFolderOriginFromId(): Unknown folder origin for folder with id '%s'", $folderid));
830
831
		return false;
832
	}
833
834
	/**
835
	 * Returns folder origin as string from its id.
836
	 *
837
	 * @param string $folderid
838
	 *
839
	 * @return string
840
	 */
841
	public static function GetFolderOriginStringFromId($folderid) {
842
		$origin = substr($folderid, 0, 1);
843
844
		switch ($origin) {
845
			case DeviceManager::FLD_ORIGIN_CONFIG:
846
				return 'configured';
847
848
			case DeviceManager::FLD_ORIGIN_GAB:
849
				return 'GAB';
850
851
			case DeviceManager::FLD_ORIGIN_SHARED:
852
				return 'shared';
853
854
			case DeviceManager::FLD_ORIGIN_USER:
855
				return 'user';
856
857
			case DeviceManager::FLD_ORIGIN_IMPERSONATED:
858
				return 'impersonated';
859
		}
860
		SLog::Write(LOGLEVEL_WARN, sprintf("Utils->GetFolderOriginStringFromId(): Unknown folder origin for folder with id '%s'", $folderid));
861
862
		return 'unknown';
863
	}
864
865
	/**
866
	 * Splits the id into folder id and message id parts. A colon in the $id indicates
867
	 * that the id has folderid:messageid format.
868
	 *
869
	 * @param string $id
870
	 *
871
	 * @return array
872
	 */
873
	public static function SplitMessageId($id) {
874
		if (strpos($id, ':') !== false) {
875
			return explode(':', $id);
876
		}
877
878
		return [null, $id];
879
	}
880
881
	/**
882
	 * Converts a string freebusy type into a numeric status.
883
	 *
884
	 * @param string $fbType
885
	 *
886
	 * @return int
887
	 */
888
	public static function GetFbStatusFromType($fbType) {
889
		switch ($fbType) {
890
			case 'Free':
891
				return fbFree;
892
893
			case 'Tentative':
894
				return fbTentative;
895
896
			case 'Busy':
897
				return fbBusy;
898
899
			case 'OOF':
900
				return fbOutOfOffice;
901
		}
902
		SLog::Write(LOGLEVEL_WARN, sprintf("Utils->GetFbStatusFromType(): Unknown free busy type '%s'", $fbType));
903
904
		return fbNoData;
905
	}
906
}
907
908
// TODO Win1252/UTF8 functions are deprecated and will be removed sometime
909
// if the ICS backend is loaded in CombinedBackend and Zarafa > 7
910
// STORE_SUPPORTS_UNICODE is true and the conversion will not be done
911
// for other backends.
912
function utf8_to_windows1252($string, $option = "", $force_convert = false) {
913
	// if the store supports unicode return the string without converting it
914
	if (!$force_convert && defined('STORE_SUPPORTS_UNICODE') && STORE_SUPPORTS_UNICODE === true) {
915
		return $string;
916
	}
917
918
	if (function_exists("iconv")) {
919
		return @iconv("UTF-8", "Windows-1252" . $option, $string);
920
	}
921
922
	return utf8_decode($string); // no euro support here
923
}
924
925
function windows1252_to_utf8($string, $option = "", $force_convert = false) {
926
	// if the store supports unicode return the string without converting it
927
	if (!$force_convert && defined('STORE_SUPPORTS_UNICODE') && STORE_SUPPORTS_UNICODE === true) {
928
		return $string;
929
	}
930
931
	if (function_exists("iconv")) {
932
		return @iconv("Windows-1252", "UTF-8" . $option, $string);
933
	}
934
935
	return utf8_encode($string); // no euro support here
936
}
937
938
function w2u($string) {
939
	return windows1252_to_utf8($string);
940
}
941
function u2w($string) {
942
	return utf8_to_windows1252($string);
943
}
944
945
function w2ui($string) {
946
	return windows1252_to_utf8($string, "//TRANSLIT");
947
}
948
function u2wi($string) {
949
	return utf8_to_windows1252($string, "//TRANSLIT");
950
}
951