```/* <![CDATA[ */
2/*
3 * Copyright 2008-2010 GuardTime AS
4 *
5 * This file is part of the GuardTime PHP SDK.
6 *
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
12 *
13 * Unless required by applicable law or agreed to in writing, software
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20/**
21 * @package util
22 */
23
24/**
25 * Collection of miscellaneous commonly used utility functions.
26 *
27 * @package util
28 */
29class GTUtil {
30
31    /**
32     * Computes the greatest common divisor (GCD) of two integers.
33     *
34     * Greatest common divisor is the largest integer that divides both numbers
35     * without remainder.
36     *
37     * <code>
38     * echo GTUtil::gcd(4, 8);
39     *
40     * // output:
41     * // 4
42     * </code>
43     *
44     * @static
45     * @param  int \$a the first integer
46     * @param  int \$b the second integer
47     * @return int the greatest common divisor of a and b or 0 if both are 0
48     */
49    public static function gcd(\$a, \$b) {
50
51        \$a = abs((int) \$a);
52        \$b = abs((int) \$b);
53
54        while (\$a > 0) {
55            \$c = \$b % \$a;
56            \$b = \$a;
57            \$a = \$c;
58        }
59
60        return \$b;
61
62    }
63
64    /**
65     * Computes the least common multiple (LCM) of two integers.
66     *
67     * Least common multiple is the smallest positive integer that can be divided
68     * by both numbers without a remainder.
69     *
70     * <code>
71     * echo GTUtil::lcm(4, 6);
72     *
73     * // output:
74     * // 12
75     * </code>
76     *
77     * @static
78     * @throws GTException when the result is too big to fit into {@code int}
79     * @param  int \$a the first integer
80     * @param  int \$b the second integer
81     * @return int the least common multiple of a and b, or 0 if either a or b is 0
82     */
83    public static function lcm(\$a, \$b) {
84
85        \$a = (int) \$a;
86        \$b = (int) \$b;
87
88        if (\$a == 0 || \$b == 0) {
89            return 0;
90        }
91
92        \$a = abs(\$a) / GTUtil::gcd(\$a, \$b);
93        \$b = abs(\$b);
94
95        if (\$a > PHP_INT_MAX / \$b) {
96            throw new GTException("Integer overflow");
97        }
98
99        return \$a * \$b;
100    }
101
102    /**
103     * Writes data to file.
104     *
105     * Example:
106     *
107     * <code>
108     * GTUtil::write('file.txt', array(1, 2, 3));
109     * </code>
110     *
111     * @static
112     * @throws GTException
113     * @param  string \$file file name to write to
114     * @param  array \$bytes byte array that contains the bytes to write
115     * @return void
116     *
118     */
119    public static function write(\$file, array \$bytes) {
120
121        if (empty(\$file)) {
122            throw new GTException("parameter file is required");
123        }
124
125        if (\$bytes == null) {
126            throw new GTException("parameter bytes is required");
127        }
128
129        if (!is_array(\$bytes)) {
130            throw new GTException("parameter bytes must be an array");
131        }
132
133        \$fp = fopen(\$file, 'wb+');
134
135        if (!\$fp) {
136            throw new GTException("Unable to open file {\$file} for writing");
137        }
138
139        if (!fwrite(\$fp, GTUtil::fromByteArray(\$bytes))) {
140            throw new GTException("Unable to write to bytes to file {\$file}");
141        }
142
143        if (!fclose(\$fp)) {
144            throw new GTException("Unable to close file after writing {\$file}");
145        }
146
147    }
148
149    /**
150     * Touches specified file.
151     *
152     * @static
153     * @throws GTException
154     * @param  \$file the file to touch
155     * @return void
156     */
157    public static function touch(\$file) {
158
159        if (empty(\$file)) {
160            throw new GTException("parameter file is required");
161        }
162
163        touch(\$file);
164    }
165
166    /**
167     * Reads data from file.
168     *
169     * @static
170     * @throws GTException
171     * @param  string \$file file name to read from
172     * @return array byte array read from \$file
173     *
174     * @see write
175     */
176    public static function read(\$file) {
177
178        if (empty(\$file)) {
179            throw new GTException("parameter file is required");
180        }
181
182        if (!is_file(\$file)) {
183            throw new GTException("file {\$file} does not exist");
184        }
185
187            throw new GTException("file {\$file} is not readable");
188        }
189
190        \$fp = fopen(\$file, 'rb');
191
192        if (!\$fp) {
193            throw new GTException("Unable to open file {\$file} for reading");
194        }
195
196        \$length = filesize(\$file);
197
198        if (\$length > 0) {
200
201        } else {
202            \$data = "";
203
204        }
205
206        if (\$data === false) {
207            throw new GTException("Unable to read from file {\$file}");
208        }
209
210        fclose(\$fp);
211
212        return GTUtil::toByteArray(\$data);
213    }
214
215    /**
216     * Wrapper method for OpenSSL asn1parse.
217     *
218     * @static
219     * @param  string \$file file containing a valid ASN.1 DER object
220     * @return void
221     */
222    public static function printAsn1(\$file) {
223        passthru("openssl asn1parse -i -inform DER -in {\$file}");
224    }
225
226    /**
227     * Decodes an ASN.1 formatted time to unix timestamp.
228     *
229     * @static
230     * @throws GTException
231     * @param  string \$time ASN.1 formatted time
232     * @param  string \$timezone timezone to use
233     * @return int php unix timestamp
234     */
235    public static function decodeTime(\$time, \$timezone = null) {
236
237        if (\$timezone != null) {
238
239            \$old_timezone = date_default_timezone_get();
240            \$new_timezone = date_default_timezone_set(\$timezone);
241
242            if (\$new_timezone === false) {
243                throw new GTException("Unable to set timezone to {\$timezone}");
244            }
245
246        }
247
248        if (\$time == null) {
249            throw new GTException("parameter time must not be empty");
250        }
251
252        if (strlen(\$time) != 15) {
253            throw new GTException("parameter time has invalid length");
254        }
255
256        \$tokens = array(
257            'year' => substr(\$time, 0, 4),
258            'month' => substr(\$time, 4, 2),
259            'day' => substr(\$time, 6, 2),
260            'hour' => substr(\$time, 8, 2),
261            'minute' => substr(\$time, 10, 2),
262            'second' => substr(\$time, 12, 2)
263        );
264
265        foreach (\$tokens as \$name => \$value) {
266            if (!ctype_digit(\$value)) {
267                throw new GTException("Invalid time encoding, {\$name} = {\$value}");
268            }
269        }
270
271        \$time = mktime(
272            (int) \$tokens['hour'],
273            (int) \$tokens['minute'],
274            (int) \$tokens['second'],
275            (int) \$tokens['month'],
276            (int) \$tokens['day'],
277            (int) \$tokens['year']
278        );
279
280        if (\$timezone != null) {
281            date_default_timezone_set(\$old_timezone);
282        }
283
284        return \$time;
285    }
286
287    /**
288     * Formats a PHP unix timestamp in a more human readable format (%Y-%m-%d %H:%M:%S UTC)
289     *
290     * @static
291     * @throws GTException
292     * @param  \$time php unix timestamp
293     * @param  \$timezone the timezone to use
294     * @return string formatted datetime
295     */
296    public static function formatTime(\$time, \$timezone = null) {
297
298        if (\$timezone != null) {
299
300            \$old_timezone = date_default_timezone_get();
301            \$new_timezone = date_default_timezone_set(\$timezone);
302
303            if (\$new_timezone === false) {
304                throw new GTException("Unable to set timezone to {\$timezone}");
305            }
306
307        }
308
309        \$formatted = strftime('%Y-%m-%d %H:%M:%S UTC', \$time);
310
311        if (\$timezone != null) {
312            date_default_timezone_set(\$old_timezone);
313        }
314
315        return \$formatted;
316    }
317
318    /**
319     * Pads \$array by prepending \$value until the size of \$array equals \$length.
320     *
321     * @static
322     * @param  \$array the array to pad
323     * @param  \$length the length to pad to
324     * @param  \$value padding value
325     * @return void
326     */
327    public static function lpad(array &\$array, \$length, \$value) {
328        while (count(\$array) < \$length) {
329            array_unshift(\$array, \$value);
330        }
331    }
332
333    /**
334     * Pads \$array by appending \$value until the size of \$array equals \$length.
335     *
336     * @static
337     * @param  \$array the array to pad
338     * @param  \$length the length to pad to
339     * @param  \$value padding value
340     * @return void
341     */
342    public static function rpad(array &\$array, \$length, \$value) {
343        while (count(\$array) < \$length) {
344            array_push(\$array, \$value);
345        }
346    }
347
348    /**
349     * Converts a string into an array of characters.
350     *
351     * <code>
352     * \$result = GTUtil::toArray('foo');
353     *
354     * print_r(\$result);
355     *
356     * // output:
357     * // Array
358     * // (
359     * //   [0] => f
360     * //   [1] => o
361     * //   [2] => o
362     * // )
363     * </code>
364     *
365     * @static
366     * @param  string \$string the input string
367     * @return array an array built from characters in string
368     */
369    public static function toArray(\$string) {
370
371        \$result = array();
372
373        if (empty(\$string)) {
374            return \$result;
375        }
376
377        for (\$i = 0; \$i < strlen(\$string); \$i++) {
378            array_push(\$result, \$string{\$i});
379        }
380
381        return \$result;
382
383    }
384
385    /**
386     * Converts an array of characters into a string
387     *
388     * <code>
389     * echo GTUtil::fromArray(array('f', 'o', 'o'));
390     *
391     * // output:
392     * // foo
393     * </code>
394     *
395     * @static
396     * @param  \$array the input array
397     * @return string a string built from characters in array
398     */
399    public static function fromArray(array \$array = null) {
400
401        \$result = "";
402
403        if (empty(\$array)) {
404            return \$result;
405        }
406
407        for (\$i = 0; \$i < count(\$array); \$i++) {
408            \$result .= \$array[\$i];
409        }
410
411        return \$result;
412
413    }
414
415    /**
416     * Converts a string into an array of bytes.
417     *
418     * Each byte in the resulting array will represent the ASCII value of each
419     * character in the input string
420     *
421     * <code>
422     * \$result = GTUtil::toByteArray('foo');
423     *
424     * print_r(\$result);
425     *
426     * // output:
427     * // Array
428     * // (
429     * //   [0] => 102
430     * //   [1] => 111
431     * //   [2] => 111
432     * // )
433     * </code>
434     *
435     * @static
436     * @param  \$string the input string
437     * @return array an array of bytes representing the characters in string
438     */
439    public static function toByteArray(\$string) {
440
441        \$result = array();
442
443        if (empty(\$string)) {
444            return \$result;
445        }
446
447        for (\$i = 0; \$i < strlen(\$string); \$i++) {
448            array_push(\$result, ord(\$string{\$i}));
449        }
450
451        return \$result;
452
453    }
454
455    /**
456     * Converts a byte array into a string.
457     *
458     * Each character in the string will represent the ASCII code of each
459     * byte in the input array
460     *
461     * <code>
462     * \$bytes = array(
463     *   ord('f'),
464     *   ord('o'),
465     *   ord('o')
466     * );
467     *
468     * echo GTUtil::fromByteArray(\$bytes);
469     *
470     * // output:
471     * // foo
472     * </code>
473     *
474     * @static
475     * @param  \$array the input array
476     * @return string a string built from bytes in the array
477     */
478    public static function fromByteArray(array \$array = null) {
479
480        \$result = "";
481
482        if (empty(\$array)) {
483            return \$result;
484        }
485
486        for (\$i = 0; \$i < count(\$array); \$i++) {
487            \$result .= chr(\$array[\$i]);
488        }
489
490        return \$result;
491
492    }
493
494    /**
495     * Reads a short (2 bytes) integer from the given byte array.
496     *
497     * @static
498     * @throws GTException
499     * @param  array \$array the byte array to read from
500     * @param  int \$position the position in the array to read from
501     * @return int short integer from the given byte array
502     */
503    public static function readShort(array \$array, \$position) {
504
505        if (\$position + 2 > count(\$array) - 1) {
506            throw new GTException("Array index out of bounds");
507        }
508
509        \$integer = new GTBigInteger(
510            array_slice(\$array, \$position, 2)
511        );
512
513        return (int) \$integer->getValue();
514    }
515
516    /**
517     * Reads an integer (4 bytes) from the given byte array.
518     *
519     * @static
520     * @throws GTException
521     * @param  array \$array the byte array to read from
522     * @param  int \$position the position in the array to read from
523     * @return int 4 byte integer from the given byte array
524     */
525    public static function readInt(array \$array, \$position) {
526
527        if (\$position + 4 > count(\$array) - 1) {
528            throw new GTException("Array index out of bounds");
529        }
530
531        \$integer = new GTBigInteger(
532            array_slice(\$array, \$position, 4)
533        );
534
535        return (int) \$integer->getValue();
536    }
537
538    /**
539     * Reads a GTBigInteger (8 bytes) from the given byte array.
540     *
541     * @static
542     * @throws GTException
543     * @param  array \$array the byte array to read from
544     * @param  int \$position the position in the array to read from
545     * @return GTBigInteger 8 byte GTBigInteger from the given byte array
546     */
547    public static function readLong(array \$array, \$position) {
548
549        if (\$position + 8 > count(\$array) - 1) {
550            throw new GTException("Array index out of bounds");
551        }
552
553        return new GTBigInteger(
554            array_slice(\$array, \$position, 8)
555        );
556    }
557
558    /**
559     * Computes and adds CRC32 to the given byte array.
560     *
561     * @static
562     * @param  \$bytes input byte array
563     * @return array bytes with added CRC32 checksum
564     */
565    public static function addCrc32(array \$bytes) {
566
567        \$checksum = crc32(GTUtil::fromByteArray(\$bytes));
568        \$checksum = sprintf("%u", \$checksum); // make unsigned, needed for 32 bit PHP
569        \$checksum = new GTBigInteger(\$checksum);
570        \$checksum = \$checksum->toBytes();
571
573        while (count(\$checksum) > 4) {
574            array_shift(\$checksum);
575        }
576
577        foreach (\$checksum as \$byte) {
578            array_push(\$bytes, \$byte);
579        }
580
581        return \$bytes;
582    }
583
584    /**
585     * Gets OpenSSL version as string.
586     *
587     * @static
588     * @return string OpenSSL version as string, example: 0.9.8
589     */
590    public static function getOpensslVersion() {
591
592        \$version = new GTBigInteger(OPENSSL_VERSION_NUMBER);
593