Conversion::restriction2json()   D
last analyzed

Complexity

Conditions 21
Paths 17

Size

Total Lines 76
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 50
c 0
b 0
f 0
nc 17
nop 1
dl 0
loc 76
rs 4.1666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Conversion utility functions for converting various data types and structures.
5
 */
6
class Conversion {
7
	/**
8
	 * Converts a COleDateTime binary string to an unix timestamp.
9
	 *
10
	 * The time component of the returned timestamp is 00:00:00
11
	 *
12
	 * @param int $cOleDateTime is a binary string which represents the number of
13
	 *                          days since 30-12-1899
14
	 *
15
	 * @return int unix timestamp of given date
16
	 */
17
	public static function COleDateTimeToUnixTime($cOleDateTime) {
18
		$days = unpack("d", $cOleDateTime);
19
20
		return ($days[1] - 25569) * 24 * 60 * 60; // 25569 is the number of days between 30-12-1899 and 01-01-1970
21
	}
22
23
	/**
24
	 * Converts an unix timestamp to a COleDateTime binary string.
25
	 *
26
	 * The time component of the unix timestamp is discarded before conversion
27
	 *
28
	 * @param int $unixtime The unix timestamp to convert
29
	 *
30
	 * @return int COleDateTime binary string representing the same day
31
	 */
32
	public static function UnixTimeToCOleDateTime($unixtime) {
33
		$days = ($unixtime / (60 * 60 * 24)) + 25569;
34
35
		return pack("d", $days);
0 ignored issues
show
Bug Best Practice introduced by
The expression return pack('d', $days) returns the type string which is incompatible with the documented return type integer.
Loading history...
36
	}
37
38
	/**
39
	 * Convert XML properties into MAPI properties.
40
	 *
41
	 * This function converts an XML array to a MAPI array. For example:
42
	 * $props["subject"] = "Meeting" --> $props[PR_SUBJECT] = "Meeting"
43
	 *
44
	 * If a mapping between a property name ('subject') and a property tag (PR_SUBJECT) cannot be found,
45
	 * it is ignored and will not be returned in the return array.
46
	 *
47
	 * The return value of this function is suitable for direct use in mapi_setprops() or related functions.
48
	 *
49
	 * Note that a conversion is done between utf-8 and windows-1252 here since all XML data is assumed to be
50
	 * utf-8 and all data written to MAPI is windows-1252.
51
	 *
52
	 * @param array &$props              Mapping of property name to MAPI property tag
53
	 * @param array &$message_properties Properties to be converted
54
	 *
55
	 * @return array MAPI property array
56
	 *
57
	 * @todo This function is slow. Since it is used very very frequently, we could get some speedup from
58
	 *       optimising this function.
59
	 * @todo This function does more than just converting, most notably doing something with 'body'. This needs
60
	 *       commenting.
61
	 */
62
	public static function mapXML2MAPI(&$props, &$message_properties) {
63
		$properties = [];
64
65
		foreach ($message_properties as $key => $value) {
66
			// HACK alert !: frontend may send back partial record (eg when setting 'read' when record is not fully loaded). Ignore for now
67
			if (isset($props[$key])) {
68
				$mapi_property = $props[$key];
69
70
				switch (mapi_prop_type($mapi_property)) {
71
					case PT_LONG:
72
						$properties[$mapi_property] = (int) $value;
73
						break;
74
75
					case PT_DOUBLE:
76
						$properties[$mapi_property] = (float) $value;
77
						break;
78
79
					case PT_MV_STRING8:
80
					case PT_MV_STRING8 | MVI_FLAG:
81
						$mv_values = explode(";", (string) $value);
82
						$values = [];
83
84
						foreach ($mv_values as $mv_value) {
85
							if (!empty($mv_value)) {
86
								$values[] = ltrim($mv_value);
87
							}
88
						}
89
90
						$properties[$mapi_property & ~MV_INSTANCE] = !empty($values) ? $values : [];
91
						break;
92
93
					case PT_MV_BINARY:
94
						// TODO: not supported at the moment
95
						break;
96
97
					case PT_BINARY:
98
						if (!empty($value)) {
99
							$properties[$mapi_property] = hex2bin((string) $value);
100
						}
101
						break;
102
103
					case PT_SRESTRICTION:
104
						if (!empty($value)) {
105
							$properties[$mapi_property] = Conversion::json2restriction($props, $value);
106
						}
107
						break;
108
109
					case PT_ACTIONS:
110
						if (!empty($value)) {
111
							$properties[$mapi_property] = Conversion::json2actions($props, $value);
112
						}
113
						break;
114
115
					default:
116
						$properties[$mapi_property] = $value;
117
						break;
118
				}
119
			}
120
		}
121
122
		// The body is a bit special, as we have to consider the HTML body and the plain-text body
123
		$isHTML = isset($message_properties["isHTML"]) && $message_properties["isHTML"] == true;
124
		if (isset($message_properties["html_body"]) && $isHTML) {
125
			$properties[PR_INTERNET_CPID] = 65001; // always write UTF-8 codepage
126
			$properties[PR_HTML] = $message_properties["html_body"];
127
		}
128
		elseif (isset($message_properties["body"]) && !$isHTML) {
129
			$properties[PR_INTERNET_CPID] = 65001; // always write UTF-8 codepage
130
			$properties[PR_BODY] = $message_properties["body"];
131
		}
132
133
		return $properties;
134
	}
135
136
	/**
137
	 * Convert MAPI properties to XML array.
138
	 *
139
	 * This function converts a MAPI property array to a XML array. For example:
140
	 * $props[PR_SUBJECT] = "Meeting" --> $props["subject"] = "Meeting"
141
	 *
142
	 * <b>WARNING</b> Properties in PT_STRING8 are assumed to be in windows-1252. This is true
143
	 * for all PT_STRING8 properties in MAPI. The data in $props is UTF-8, so the conversion is done
144
	 * here.
145
	 *
146
	 * Conversion is done as the reverse of mapXML2MAPI.
147
	 *
148
	 * @see Conversion::mapXML2MAPI()
149
	 *
150
	 * @param array &$props              List of MAPI properties
151
	 * @param array &$message_properties MAPI array
152
	 *
153
	 * @return array list of properties which will be sent back to the client
154
	 */
155
	public static function mapMAPI2XML(&$props, &$message_properties) {
156
		$idProps = [PR_ENTRYID => 1, PR_PARENT_ENTRYID => 1, PR_STORE_ENTRYID => 1, PR_RULE_ID => 1];
157
		$properties = ["props" => []];
158
159
		foreach ($props as $name => $property) {
160
			if (isset($message_properties[$property & ~MVI_FLAG])) {
161
				$property = ($property & ~MVI_FLAG);
162
			}
163
			elseif (!isset($message_properties[$property])) {
164
				continue;
165
			}
166
167
			// inlined below function for speed (only used here anyway)
168
			$value = $message_properties[$property];
169
170
			switch (mapi_prop_type($property)) {
171
				case PT_BINARY:
172
					$value = bin2hex((string) $value);
173
					break;
174
175
				case PT_MV_BINARY:
176
					$value = array_map("bin2hex", $value);
177
					break;
178
179
				case PT_MV_STRING8:
180
					$result = "";
181
					foreach ($value as $entry) {
182
						if (!empty($entry)) {
183
							$result .= $entry . "; ";
184
						}
185
					}
186
					$value = $result;
187
					break;
188
189
				case PT_SRESTRICTION:
190
					$value = Conversion::restriction2json($value);
191
					break;
192
193
				case PT_ACTIONS:
194
					$value = Conversion::actions2json($value);
195
					break;
196
			}
197
198
			if (isset($idProps[$property])) {
199
				// put identification properties to root level
200
				$properties[$name] = $value;
201
			}
202
			else {
203
				$properties["props"][$name] = $value;
204
			}
205
		}
206
207
		if (empty($properties["props"])) {
208
			unset($properties["props"]);
209
		}
210
211
		return $properties;
212
	}
213
214
	/* JSON related functions */
215
216
	/**
217
	 * Convert a JSON reference to a property into a MAPI property tag.
218
	 *
219
	 * Note that this depends on the definition of the property tag constants
220
	 * in mapitags.php
221
	 * Mappings are defined per module in class.properties.php
222
	 *
223
	 * @example json2property('subject') => 0x0037001e
224
	 * @example this also works json2property('PR_SUBJECT') => 0x0037001e
225
	 *
226
	 * @param array  $mapping               props A mapping of property names to their corresponding MAPI property tags
227
	 * @param string $prop                  property name
228
	 * @param bool   $convertToSingleValued whether to convert the multi valued property tag to single valued or not
229
	 *
230
	 * @return int The property tag
231
	 */
232
	public static function json2property($mapping, $prop, $convertToSingleValued = false) {
233
		$propTag = false;
234
235
		if (isset($mapping[$prop])) {
236
			// Normally we would go for this case, where the property is defined in our mapping
237
			$propTag = $mapping[$prop];
238
		}
239
		elseif (defined($prop)) {
240
			// This is for cases where the proptag reference is defined as a constant, e.g. PR_BODY
241
			$propTag = constant($prop);
242
		}
243
		elseif (substr_count($prop, ':') == 2) {
244
			// This is for named properties, e.g. 'PT_STRING8:PSETID_Address:0x8005'
245
			$props = getPropIdsFromStrings($GLOBALS["mapisession"]->getDefaultMessageStore(), [$prop]);
246
			$propTag = $props[0];
247
		}
248
		else {
249
			// In this case we expect the string to be a hex id of the proptag
250
			$propTag = hexdec($prop);
251
		}
252
253
		// if getting multi valued property then sometimes we need to convert it to
254
		// its single valued counterpart to use in VALUES structure of restriction
255
		if ($convertToSingleValued) {
256
			$propTag = Conversion::convertToSingleValuedProperty($propTag);
257
		}
258
259
		return $propTag;
260
	}
261
262
	/**
263
	 * Convert a MAPI property tag into a JSON reference.
264
	 *
265
	 * Note that this depends on the definition of the property tag constants
266
	 * in mapitags.php
267
	 *
268
	 * @example property2json(0x0037001e) => 'PR_SUBJECT'
269
	 *
270
	 * @param int The property tag
271
	 * @param mixed $property
272
	 *
273
	 * @return string the symbolic name of the property tag
274
	 */
275
	public static function property2json($property) {
276
		if (is_integer($property)) {
277
			// Retrieve constants categories, zcore provides them in 'Core'
278
			foreach (get_defined_constants(true)['Core'] as $key => $value) {
279
				if ($property == $value && str_starts_with($key, 'PR_')) {
280
					return $key;
281
				}
282
			}
283
284
			return sprintf("0x%08X", $property);
285
		}
286
287
		return $property;
288
	}
289
290
	/**
291
	 * Convert an JSON restriction structure into a MAPI SRestriction array.
292
	 *
293
	 * @param array $mapping An associative array mapping property keys to MAPI proptags
294
	 * @param array $json    The parsed JSON array data
295
	 *
296
	 * @return array MAPI restriction array compatible with MAPI extension restriction format
297
	 */
298
	public static function json2restriction($mapping, $json) {
299
		if (!is_array($json)) {
0 ignored issues
show
introduced by
The condition is_array($json) is always true.
Loading history...
300
			return $json;
301
		}
302
303
		switch ($json[0]) {
304
			case RES_AND:
305
			case RES_OR:
306
				if (isset($json[1][0]) && is_array($json[1][0])) {
307
					foreach ($json[1] as &$res) {
308
						$res = Conversion::json2restriction($mapping, $res);
309
					}
310
					unset($res);
311
				}
312
				elseif (!empty($json[1])) {
313
					$json[1] = Conversion::json2restriction($mapping, $json[1]);
314
				}
315
				break;
316
317
			case RES_NOT:
318
				// Note that a normal RES_NOT restriction should not be placed
319
				// in an array, but the php-ext does expect an array to be
320
				// given, so we wrap it here.
321
				$json[1] = [Conversion::json2restriction($mapping, $json[1])];
322
				break;
323
324
			case RES_COMMENT:
325
				$props = [];
326
				foreach ($json[1][PROPS] as $propTag => $propValue) {
327
					$propTag = Conversion::json2property($mapping, $propTag);
328
					if (mapi_prop_type($propTag) === PT_BINARY) {
329
						$propValue = hex2bin((string) $propValue);
330
					}
331
					$props[$propTag] = $propValue;
332
				}
333
334
				$json[1][PROPS] = $props;
335
				$json[1][RESTRICTION] = Conversion::json2restriction($mapping, $json[1][RESTRICTION]);
336
				break;
337
338
			case RES_PROPERTY:
339
			case RES_CONTENT:
340
				$json[1][ULPROPTAG] = Conversion::json2property($mapping, $json[1][ULPROPTAG]);
341
				$value = [];
342
343
				foreach ($json[1][VALUE] as $propTag => $propValue) {
344
					$propTag = Conversion::json2property($mapping, $propTag);
345
					$type = mapi_prop_type($propTag);
346
347
					if ($type === PT_BINARY) {
348
						$propValue = hex2bin((string) $propValue);
349
					}
350
					elseif ($type === PT_MV_STRING8) {
351
						// Convert multivalued strings to arrays
352
						$mv_values = explode(";", (string) $propValue);
353
						$values = [];
354
355
						foreach ($mv_values as $mv_value) {
356
							if (!empty($mv_value)) {
357
								$values[] = ltrim($mv_value);
358
							}
359
						}
360
361
						$propValue = !empty($values) ? $values : [];
362
					}
363
364
					$value[$propTag] = $propValue;
365
				}
366
367
				unset($json[1][VALUE]);
368
				$json[1][VALUE] = $value;
369
				break;
370
371
			case RES_COMPAREPROPS:
372
				$json[1][ULPROPTAG1] = Conversion::json2property($mapping, $json[1][ULPROPTAG1]);
373
				$json[1][ULPROPTAG2] = Conversion::json2property($mapping, $json[1][ULPROPTAG2]);
374
				break;
375
376
			case RES_BITMASK:
377
			case RES_SIZE:
378
			case RES_EXIST:
379
				$json[1][ULPROPTAG] = Conversion::json2property($mapping, $json[1][ULPROPTAG]);
380
				break;
381
382
			case RES_SUBRESTRICTION:
383
				$json[1][ULPROPTAG] = Conversion::json2property($mapping, $json[1][ULPROPTAG]);
384
				$json[1][RESTRICTION] = Conversion::json2restriction($mapping, $json[1][RESTRICTION]);
385
				break;
386
		}
387
388
		return $json;
389
	}
390
391
	/**
392
	 * Convert a MAPI SRestriction array into an JSON restriction structure.
393
	 *
394
	 * @param mixed $restriction The MAPI Restriction array
395
	 *
396
	 * @return array Array structure that can be easily serialized to JSON format
397
	 */
398
	public static function restriction2json($restriction) {
399
		if (!is_array($restriction)) {
400
			return $restriction;
401
		}
402
403
		switch ($restriction[0]) {
404
			case RES_AND:
405
			case RES_OR:
406
				if (!empty($restriction[1][0])) {
407
					foreach ($restriction[1] as &$res) {
408
						$res = Conversion::restriction2json($res);
409
					}
410
					unset($res);
411
				}
412
				elseif (!empty($restriction[1])) {
413
					$restriction[1] = Conversion::restriction2json($restriction[1]);
414
				}
415
				break;
416
417
			case RES_NOT:
418
				// Note that a normal RES_NOT restriction should not be placed
419
				// in an array, but the php-ext does put it in one, so unwrap it here.
420
				$restriction[1] = Conversion::restriction2json($restriction[1][0]);
421
				break;
422
423
			case RES_COMMENT:
424
				$restriction[1][RESTRICTION] = Conversion::restriction2json($restriction[1][RESTRICTION]);
425
				$props = [];
426
427
				foreach ($restriction[1][PROPS] as $propTag => $propValue) {
428
					if (mapi_prop_type($propTag) === PT_BINARY) {
429
						$propValue = bin2hex((string) $propValue);
430
					}
431
					$props[Conversion::property2json($propTag)] = is_array($propValue) ? $propValue[$propTag] : $propValue;
432
				}
433
434
				unset($restriction[1][PROPS]);
435
				$restriction[1][PROPS] = $props;
436
				break;
437
438
			case RES_COMPAREPROPS:
439
				$restriction[1][ULPROPTAG1] = Conversion::property2json($restriction[1][ULPROPTAG1]);
440
				$restriction[1][ULPROPTAG2] = Conversion::property2json($restriction[1][ULPROPTAG2]);
441
				break;
442
443
			case RES_PROPERTY:
444
			case RES_CONTENT:
445
				$restriction[1][ULPROPTAG] = Conversion::property2json($restriction[1][ULPROPTAG]);
446
				$value = [];
447
448
				foreach ($restriction[1][VALUE] as $propTag => $propValue) {
449
					if (mapi_prop_type($propTag) === PT_BINARY) {
450
						$propValue = bin2hex((string) $propValue);
451
					}
452
453
					$value[Conversion::property2json($propTag)] = $propValue;
454
				}
455
456
				// remove non restriction data
457
				unset($restriction[1][VALUE]);
458
				$restriction[1][VALUE] = $value;
459
				break;
460
461
			case RES_BITMASK:
462
			case RES_SIZE:
463
			case RES_EXIST:
464
				$restriction[1][ULPROPTAG] = Conversion::property2json($restriction[1][ULPROPTAG]);
465
				break;
466
467
			case RES_SUBRESTRICTION:
468
				$restriction[1][ULPROPTAG] = Conversion::property2json($restriction[1][ULPROPTAG]);
469
				$restriction[1][RESTRICTION] = Conversion::restriction2json($restriction[1][RESTRICTION]);
470
				break;
471
		}
472
473
		return $restriction;
474
	}
475
476
	/**
477
	 * Retrieves singlevalued counterpart of a multivalued property.
478
	 *
479
	 * Multivalued properties has different property tags in VALUES part
480
	 * so we need to find that singlevalued property tag
481
	 *
482
	 * @param int $propTag The multivalued property tag in string
483
	 *
484
	 * @return int The singlevalued property tag
485
	 */
486
	public static function convertToSingleValuedProperty($propTag) {
487
		if (is_string($propTag)) {
0 ignored issues
show
introduced by
The condition is_string($propTag) is always false.
Loading history...
488
			// if the string starts with '0x' then we have
489
			// an acceptable propTag on which we can work
490
			// to change the MV_flag value
491
			if (str_starts_with($propTag, '0x')) {
492
				$propTag = hexdec($propTag);
493
				$propTag &= ~MV_FLAG;
494
				$propTag = '0x' . strtoupper(str_pad((string) dechex_32($propTag), 8, '0', STR_PAD_LEFT));
495
			}
496
		}
497
		else {
498
			// If numeric, just remove the MV_FLAG
499
			$propTag &= ~MV_FLAG;
500
		}
501
502
		return $propTag;
503
	}
504
505
	/**
506
	 * Converts a JSON Representation of PT_ACTIONS value array to its MAPI form.
507
	 *
508
	 * @param array $actions JSON representation PT_ACTIONS structure
509
	 * @param mixed $mapping
510
	 *
511
	 * @return array Converted PT_ACTIONS structure that can be used in MAPI
512
	 */
513
	public static function json2actions($mapping, $actions) {
514
		foreach ($actions as &$action) {
515
			foreach ($action as $key => $value) {
516
				switch ($key) {
517
					case 'storeentryid':
518
					case 'folderentryid':
519
					case 'replyentryid':
520
					case 'replyguid':
521
					case 'dam':
522
						$action[$key] = hex2bin((string) $value);
523
						break;
524
525
					case 'adrlist':
526
						$recipients = [];
527
528
						foreach ($value as $addr) {
529
							$recipient = [];
530
531
							foreach ($addr as $addrkey => $addrval) {
532
								$addrkey = Conversion::json2property($mapping, $addrkey);
533
534
								if (mapi_prop_type($addrkey) === PT_BINARY) {
535
									$addrval = hex2bin((string) $addrval);
536
								}
537
538
								$recipient[$addrkey] = $addrval;
539
							}
540
							unset($addrval);
541
542
							$recipients[] = $recipient;
543
						}
544
						unset($addr);
545
546
						$action[$key] = $recipients;
547
						break;
548
				}
549
			}
550
		}
551
552
		return $actions;
553
	}
554
555
	/**
556
	 * Converts a MAPI PT_ACTIONS value array to its JSON representation.
557
	 *
558
	 * @param array $actions MAPI PT_ACTIONS structure
559
	 *
560
	 * @return array Converted PT_ACTIONS structure that can be used in javascript
561
	 */
562
	public static function actions2json($actions) {
563
		foreach ($actions as &$action) {
564
			foreach ($action as $key => $value) {
565
				switch ($key) {
566
					case 'storeentryid':
567
					case 'folderentryid':
568
					case 'replyentryid':
569
					case 'replyguid':
570
					case 'dam':
571
						$action[$key] = bin2hex((string) $value);
572
						break;
573
574
					case 'adrlist':
575
						$recipients = [];
576
577
						foreach ($value as $addr) {
578
							$recipient = [];
579
580
							foreach ($addr as $addrkey => $addrval) {
581
								if (mapi_prop_type($addrkey) === PT_BINARY) {
582
									$addrval = bin2hex((string) $addrval);
583
								}
584
585
								$recipient[Conversion::property2json($addrkey)] = $addrval;
586
							}
587
							unset($addrval);
588
589
							$recipients[] = $recipient;
590
						}
591
						unset($addr);
592
593
						$action[$key] = $recipients;
594
						break;
595
				}
596
			}
597
		}
598
599
		return $actions;
600
	}
601
602
	/**
603
	 * Mapping of code page identifiers to character sets
604
	 * Table taken from common/codepage.cpp.
605
	 *
606
	 * @see http://msdn.microsoft.com/en-us/library/dd317756(VS.85).aspx
607
	 * Note: The order is done like this because we use this array in the findCharSet
608
	 * function, which is called when a message identifies itself with a charset that
609
	 * it isn't in. Because this mostly happens with iso-8859-1 that says they are in
610
	 * utf-8, we use this order to first check if it isn't iso-8859-1.
611
	 *
612
	 * @property
613
	 */
614
	private static $_CODEPAGES = [
615
		37 => "IBM037",
616
		437 => "IBM437",
617
		500 => "IBM500",
618
		708 => "ASMO-708",
619
		720 => "DOS-720",
620
		737 => "ibm737",
621
		775 => "ibm775",
622
		850 => "ibm850",
623
		852 => "ibm852",
624
		855 => "IBM855",
625
		857 => "ibm857",
626
		858 => "IBM00858",
627
		860 => "IBM860",
628
		861 => "ibm861",
629
		862 => "DOS-862",
630
		863 => "IBM863",
631
		864 => "IBM864",
632
		865 => "IBM865",
633
		866 => "cp866",
634
		869 => "ibm869",
635
		870 => "IBM870",
636
		874 => "windows-874",
637
		875 => "cp875",
638
		932 => "shift_jis",
639
		936 => "gb2312",
640
		936 => "gbk",
641
		949 => "cp949",
642
		950 => "big5",
643
		1026 => "IBM1026",
644
		1047 => "IBM01047",
645
		1140 => "IBM01140",
646
		1141 => "IBM01141",
647
		1142 => "IBM01142",
648
		1143 => "IBM01143",
649
		1144 => "IBM01144",
650
		1145 => "IBM01145",
651
		1146 => "IBM01146",
652
		1147 => "IBM01147",
653
		1148 => "IBM01148",
654
		1149 => "IBM01149",
655
		1200 => "utf-16",
656
		1201 => "unicodeFFFE",
657
		1250 => "windows-1250",
658
		1251 => "windows-1251",
659
		1252 => "windows-1252",
660
		1253 => "windows-1253",
661
		1254 => "windows-1254",
662
		1255 => "windows-1255",
663
		1256 => "windows-1256",
664
		1257 => "windows-1257",
665
		1258 => "windows-1258",
666
		1361 => "Johab",
667
		10000 => "macintosh",
668
		10001 => "x-mac-japanese",
669
		10002 => "x-mac-chinesetrad",
670
		10003 => "x-mac-korean",
671
		10004 => "x-mac-arabic",
672
		10005 => "x-mac-hebrew",
673
		10006 => "x-mac-greek",
674
		10007 => "x-mac-cyrillic",
675
		10008 => "x-mac-chinesesimp",
676
		10010 => "x-mac-romanian",
677
		10017 => "x-mac-ukrainian",
678
		10021 => "x-mac-thai",
679
		10029 => "x-mac-ce",
680
		10079 => "x-mac-icelandic",
681
		10081 => "x-mac-turkish",
682
		10082 => "x-mac-croatian",
683
		12000 => "utf-32",
684
		12001 => "utf-32BE",
685
		20000 => "x-Chinese_CNS",
686
		20001 => "x-cp20001",
687
		20002 => "x_Chinese-Eten",
688
		20003 => "x-cp20003",
689
		20004 => "x-cp20004",
690
		20005 => "x-cp20005",
691
		20105 => "x-IA5",
692
		20106 => "x-IA5-German",
693
		20107 => "x-IA5-Swedish",
694
		20108 => "x-IA5-Norwegian",
695
		20127 => "us-ascii",
696
		20261 => "x-cp20261",
697
		20269 => "x-cp20269",
698
		20273 => "IBM273",
699
		20277 => "IBM277",
700
		20278 => "IBM278",
701
		20280 => "IBM280",
702
		20284 => "IBM284",
703
		20285 => "IBM285",
704
		20290 => "IBM290",
705
		20297 => "IBM297",
706
		20420 => "IBM420",
707
		20423 => "IBM423",
708
		20424 => "IBM424",
709
		20833 => "x-EBCDIC-KoreanExtended",
710
		20838 => "IBM-Thai",
711
		20866 => "koi8-r",
712
		20871 => "IBM871",
713
		20880 => "IBM880",
714
		20905 => "IBM905",
715
		20924 => "IBM00924",
716
		20932 => "EUC-JP",
717
		20936 => "x-cp20936",
718
		20949 => "x-cp20949",
719
		21025 => "cp1025",
720
		21866 => "koi8-u",
721
		28591 => "iso-8859-1",
722
		28592 => "iso-8859-2",
723
		28593 => "iso-8859-3",
724
		28594 => "iso-8859-4",
725
		28595 => "iso-8859-5",
726
		28596 => "iso-8859-6",
727
		28597 => "iso-8859-7",
728
		28598 => "iso-8859-8",
729
		28599 => "iso-8859-9",
730
		28603 => "iso-8859-13",
731
		28605 => "iso-8859-15",
732
		29001 => "x-Europa",
733
		38598 => "iso-8859-8-i",
734
		50220 => "iso-2022-jp",
735
		50221 => "csISO2022JP",
736
		50222 => "iso-2022-jp",
737
		50225 => "iso-2022-kr",
738
		50227 => "x-cp50227",
739
		51932 => "euc-jp",
740
		51936 => "EUC-CN",
741
		51949 => "euc-kr",
742
		52936 => "hz-gb-2312",
743
		54936 => "GB18030",
744
		57002 => "x-iscii-de",
745
		57003 => "x-iscii-be",
746
		57004 => "x-iscii-ta",
747
		57005 => "x-iscii-te",
748
		57006 => "x-iscii-as",
749
		57007 => "x-iscii-or",
750
		57008 => "x-iscii-ka",
751
		57009 => "x-iscii-ma",
752
		57010 => "x-iscii-gu",
753
		57011 => "x-iscii-pa",
754
		65000 => "utf-7",
755
		65001 => "utf-8",
756
	];
757
758
	/**
759
	 * Get charset name from a codepage.
760
	 *
761
	 * @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...
762
	 * @param mixed $codepage
763
	 *
764
	 * @return string iconv-compatible charset name
765
	 */
766
	public static function getCodepageCharset($codepage) {
767
		// Defaulting to iso-8859-15 since it is more likely for someone to make a mistake in the codepage
768
		// when using west-european charsets then when using other charsets since utf-8 is binary compatible
769
		// with the bottom 7 bits of west-european
770
		return Conversion::$_CODEPAGES[$codepage] ?? "iso-8859-15";
771
	}
772
773
	/**
774
	 * Tries to find the character set of a given string.
775
	 *
776
	 * @param string $string The string for which a charset is requested
777
	 *
778
	 * @return string
779
	 */
780
	private static function findCharSet($string) {
781
		// Let's try mb_detect_encoding first
782
		$charset = mb_detect_encoding($string, "auto");
783
784
		if ($charset !== false) {
785
			return $charset;
786
		}
787
788
		// Trying every encoding from $_CODEPAGES is pointless. There
789
		// is at least one encoding where all imaginable input byte
790
		// sequences are valid, and a heuristic would pick that even if
791
		// it is completely inaccurate. So just return a fixed charset
792
		// here.
793
		//
794
		// With
795
		// https://en.wikipedia.org/wiki/Popularity_of_text_encodings
796
		// in mind, we want to pick an ASCII-compatible encoding so
797
		// that not everything becomes illegible (like with "cp037"),
798
		// but where codepage mismatch is otherwise apparent. This
799
		// necessitates a non-poular charset, so iso-8859-1/15,
800
		// cp1251/1252, utf-8 are all ruled out.
801
		//
802
		return "cp850";
803
	}
804
805
	/**
806
	 * Converts a string with a given codepage to utf-8.
807
	 *
808
	 * @param int    $codepage The codepage of the given string
809
	 * @param string $string   The string that must be converted
810
	 *
811
	 * @return string
812
	 */
813
	public static function convertCodepageStringToUtf8($codepage, $string) {
814
		$charset = Conversion::getCodepageCharset($codepage);
815
816
		// Sometimes a mail identifies itself with a charset that it really isn't, so let's do an extra check
817
		// Only do the extra check if iconv was able to convert string. See grommunio-web#142
818
		$converted = iconv($charset, $charset, $string);
819
		if ($converted !== false && strlen($converted) !== strlen($string)) {
820
			$foundCharset = Conversion::findCharSet($string);
821
			if ($foundCharset) {
822
				$charset = $foundCharset;
823
			}
824
		}
825
826
		return iconv($charset, "utf-8", $string);
827
	}
828
}
829