Aleksander Machniak
2015-11-18 5143c47e0feeff92ac3dabf9277e23c13a6379f0
commit | author | age
c83b83 1 <?php
AM 2
3 /**
4  * Test class to test rcube_utils class
5  *
6  * @package Tests
7  */
9b05f1 8 class Framework_Utils extends PHPUnit_Framework_TestCase
c83b83 9 {
AM 10
11     /**
12      * Valid email addresses for test_valid_email()
13      */
14     function data_valid_email()
15     {
16         return array(
17             array('email@domain.com', 'Valid email'),
18             array('firstname.lastname@domain.com', 'Email contains dot in the address field'),
19             array('email@subdomain.domain.com', 'Email contains dot with subdomain'),
20             array('firstname+lastname@domain.com', 'Plus sign is considered valid character'),
da2812 21             array('email@[123.123.123.123]', 'Square bracket around IP address'),
AM 22             array('email@[IPv6:::1]', 'Square bracket around IPv6 address (1)'),
23             array('email@[IPv6:::1.2.3.4]', 'Square bracket around IPv6 address (2)'),
24             array('email@[IPv6:2001:2d12:c4fe:5afe::1]', 'Square bracket around IPv6 address (3)'),
c83b83 25             array('"email"@domain.com', 'Quotes around email is considered valid'),
AM 26             array('1234567890@domain.com', 'Digits in address are valid'),
27             array('email@domain-one.com', 'Dash in domain name is valid'),
28             array('_______@domain.com', 'Underscore in the address field is valid'),
29             array('email@domain.name', '.name is valid Top Level Domain name'),
30             array('email@domain.co.jp', 'Dot in Top Level Domain name also considered valid (use co.jp as example here)'),
31             array('firstname-lastname@domain.com', 'Dash in address field is valid'),
848e20 32             array('test@xn--e1aaa0cbbbcacac.xn--p1ai', 'IDNA domain'),
c83b83 33         );
AM 34     }
35
36     /**
37      * Invalid email addresses for test_invalid_email()
38      */
39     function data_invalid_email()
40     {
41         return array(
42             array('plainaddress', 'Missing @ sign and domain'),
43             array('#@%^%#$@#$@#.com', 'Garbage'),
44             array('@domain.com', 'Missing username'),
45             array('Joe Smith <email@domain.com>', 'Encoded html within email is invalid'),
46             array('email.domain.com', 'Missing @'),
47             array('email@domain@domain.com', 'Two @ sign'),
48             array('.email@domain.com', 'Leading dot in address is not allowed'),
49             array('email.@domain.com', 'Trailing dot in address is not allowed'),
50             array('email..email@domain.com', 'Multiple dots'),
51             array('あいうえお@domain.com', 'Unicode char as address'),
52             array('email@domain.com (Joe Smith)', 'Text followed email is not allowed'),
53             array('email@domain', 'Missing top level domain (.com/.net/.org/etc)'),
54             array('email@-domain.com', 'Leading dash in front of domain is invalid'),
55 //            array('email@domain.web', '.web is not a valid top level domain'),
da2812 56             array('email@123.123.123.123', 'IP address without brackets'),
AM 57             array('email@2001:2d12:c4fe:5afe::1', 'IPv6 address without brackets'),
58             array('email@IPv6:2001:2d12:c4fe:5afe::1', 'IPv6 address without brackets (2)'),
59             array('email@[111.222.333.44444]', 'Invalid IP format'),
60             array('email@[111.222.255.257]', 'Invalid IP format (2)'),
61             array('email@[.222.255.257]', 'Invalid IP format (3)'),
62             array('email@[::1]', 'Invalid IPv6 format (1)'),
63             array('email@[IPv6:2001:23x2:1]', 'Invalid IPv6 format (2)'),
64             array('email@[IPv6:1111:2222:33333::4444:5555]', 'Invalid IPv6 format (3)'),
65             array('email@[IPv6:1111::3333::4444:5555]', 'Invalid IPv6 format (4)'),
c83b83 66             array('email@domain..com', 'Multiple dot in the domain portion is invalid'),
AM 67         );
68     }
69
70     /**
71      * @dataProvider data_valid_email
72      */
73     function test_valid_email($email, $title)
74     {
75         $this->assertTrue(rcube_utils::check_email($email, false), $title);
76     }
77
78     /**
79      * @dataProvider data_invalid_email
80      */
81     function test_invalid_email($email, $title)
82     {
83         $this->assertFalse(rcube_utils::check_email($email, false), $title);
84     }
85
9b05f1 86     /**
a65ce5 87      * Valid IP addresses for test_valid_ip()
AM 88      */
89     function data_valid_ip()
90     {
91         return array(
92             array('0.0.0.0'),
93             array('123.123.123.123'),
94             array('::'),
95             array('::1'),
96             array('::1.2.3.4'),
97             array('2001:2d12:c4fe:5afe::1'),
98         );
99     }
100
101     /**
102      * Valid IP addresses for test_invalid_ip()
103      */
104     function data_invalid_ip()
105     {
106         return array(
107             array(''),
108             array(0),
109             array('123.123.123.1234'),
110             array('1.1.1.1.1'),
111             array('::1.2.3.260'),
112             array('::1.0'),
113             array('2001::c4fe:5afe::1'),
114         );
115     }
116
117     /**
118      * @dataProvider data_valid_ip
119      */
120     function test_valid_ip($ip)
121     {
122         $this->assertTrue(rcube_utils::check_ip($ip));
123     }
124
125     /**
126      * @dataProvider data_invalid_ip
127      */
128     function test_invalid_ip($ip)
129     {
130         $this->assertFalse(rcube_utils::check_ip($ip));
131     }
132
133     /**
5f8097 134      * Data for test_rep_specialchars_output()
AM 135      */
136     function data_rep_specialchars_output()
137     {
138         return array(
139             array('', '', 'abc', 'abc'),
140             array('', '', '?', '?'),
141             array('', '', '"', '&quot;'),
142             array('', '', '<', '&lt;'),
143             array('', '', '>', '&gt;'),
144             array('', '', '&', '&amp;'),
145             array('', '', '&amp;', '&amp;amp;'),
146             array('', '', '<a>', '&lt;a&gt;'),
147             array('', 'remove', '<a>', ''),
148         );
149     }
150
151     /**
152      * Test for rep_specialchars_output
153      * @dataProvider data_rep_specialchars_output
154      */
155     function test_rep_specialchars_output($type, $mode, $str, $res)
156     {
157         $result = rcube_utils::rep_specialchars_output(
158             $str, $type ? $type : 'html', $mode ? $mode : 'strict');
159
160         $this->assertEquals($result, $res);
161     }
162
163     /**
9b05f1 164      * rcube_utils::mod_css_styles()
AM 165      */
166     function test_mod_css_styles()
167     {
168         $css = file_get_contents(TESTS_DIR . 'src/valid.css');
169         $mod = rcube_utils::mod_css_styles($css, 'rcmbody');
170
171         $this->assertRegExp('/#rcmbody\s+\{/', $mod, "Replace body style definition");
172         $this->assertRegExp('/#rcmbody h1\s\{/', $mod, "Prefix tag styles (single)");
173         $this->assertRegExp('/#rcmbody h1, #rcmbody h2, #rcmbody h3, #rcmbody textarea\s+\{/', $mod, "Prefix tag styles (multiple)");
174         $this->assertRegExp('/#rcmbody \.noscript\s+\{/', $mod, "Prefix class styles");
fdb30f 175
TB 176         $css = file_get_contents(TESTS_DIR . 'src/media.css');
177         $mod = rcube_utils::mod_css_styles($css, 'rcmbody');
178
179         $this->assertContains('#rcmbody table[class=w600]', $mod, 'Replace styles nested in @media block');
180         $this->assertContains('#rcmbody {width:600px', $mod, 'Replace body selector nested in @media block');
9b05f1 181     }
AM 182
183     /**
184      * rcube_utils::mod_css_styles()
185      */
186     function test_mod_css_styles_xss()
187     {
188         $mod = rcube_utils::mod_css_styles("body.main2cols { background-image: url('../images/leftcol.png'); }", 'rcmbody');
189         $this->assertEquals("/* evil! */", $mod, "No url() values allowed");
190
191         $mod = rcube_utils::mod_css_styles("@import url('http://localhost/somestuff/css/master.css');", 'rcmbody');
192         $this->assertEquals("/* evil! */", $mod, "No import statements");
193
194         $mod = rcube_utils::mod_css_styles("left:expression(document.body.offsetWidth-20)", 'rcmbody');
195         $this->assertEquals("/* evil! */", $mod, "No expression properties");
196
197         $mod = rcube_utils::mod_css_styles("left:exp/*  */ression( alert(&#039;xss3&#039;) )", 'rcmbody');
198         $this->assertEquals("/* evil! */", $mod, "Don't allow encoding quirks");
199
200         $mod = rcube_utils::mod_css_styles("background:\\0075\\0072\\006c( javascript:alert(&#039;xss&#039;) )", 'rcmbody');
201         $this->assertEquals("/* evil! */", $mod, "Don't allow encoding quirks (2)");
202     }
3a54cc 203
AM 204     /**
5383ad 205      * Check rcube_utils::explode_quoted_string()
AM 206      */
207     function test_explode_quoted_string()
208     {
209         $data = array(
210             '"a,b"' => array('"a,b"'),
211             '"a,b","c,d"' => array('"a,b"','"c,d"'),
212             '"a,\\"b",d' => array('"a,\\"b"', 'd'),
213         );
214
215         foreach ($data as $text => $res) {
216             $result = rcube_utils::explode_quoted_string(',', $text);
217             $this->assertSame($res, $result);
218         }
219     }
220
221     /**
3a54cc 222      * Check rcube_utils::explode_quoted_string() compat. with explode()
AM 223      */
224     function test_explode_quoted_string_compat()
225     {
226         $data = array('', 'a,b,c', 'a', ',', ',a');
227
228         foreach ($data as $text) {
229             $result = rcube_utils::explode_quoted_string(',', $text);
230             $this->assertSame(explode(',', $text), $result);
231         }
232     }
0c82e9 233
AM 234     /**
235      * rcube_utils::get_boolean()
236      */
237     function test_get_boolean()
238     {
239         $input = array(
240             false, 'false', '0', 'no', 'off', 'nein', 'FALSE', '', null,
241         );
242
243         foreach ($input as $idx => $value) {
11eb07 244             $this->assertFalse(rcube_utils::get_boolean($value), "Invalid result for $idx test item");
0c82e9 245         }
AM 246
247         $input = array(
248             true, 'true', '1', 1, 'yes', 'anything', 1000,
249         );
250
251         foreach ($input as $idx => $value) {
11eb07 252             $this->assertTrue(rcube_utils::get_boolean($value), "Invalid result for $idx test item");
0c82e9 253         }
AM 254     }
255
fe0f1d 256     /**
AM 257      * rcube:utils::file2class()
258      */
259     function test_file2class()
260     {
261         $test = array(
262             array('', '', 'unknown'),
263             array('text', 'text', 'text'),
264             array('image/png', 'image.png', 'image png'),
265         );
266
267         foreach ($test as $v) {
268             $result = rcube_utils::file2class($v[0], $v[1]);
269             $this->assertSame($v[2], $result);
270         }
271     }
896e2b 272
AM 273     /**
274      * rcube:utils::strtotime()
275      */
276     function test_strtotime()
277     {
04e767 278         // this test depends on system timezone if not set
AM 279         date_default_timezone_set('UTC');
280
896e2b 281         $test = array(
AM 282             '1' => 1,
283             '' => 0,
09c58d 284             'abc-555' => 0,
04e767 285             '2013-04-22' => 1366588800,
AM 286             '2013/04/22' => 1366588800,
287             '2013.04.22' => 1366588800,
288             '22-04-2013' => 1366588800,
289             '22/04/2013' => 1366588800,
290             '22.04.2013' => 1366588800,
291             '22.4.2013'  => 1366588800,
292             '20130422'   => 1366588800,
09c58d 293             '2013/06/21 12:00:00 UTC' => 1371816000,
TB 294             '2013/06/21 12:00:00 Europe/Berlin' => 1371808800,
896e2b 295         );
AM 296
297         foreach ($test as $datetime => $ts) {
298             $result = rcube_utils::strtotime($datetime);
b32fab 299             $this->assertSame($ts, $result, "Error parsing date: $datetime");
896e2b 300         }
AM 301     }
302
303     /**
b1f3c3 304      * rcube:utils::anytodatetime()
AM 305      */
306     function test_anytodatetime()
307     {
308         $test = array(
309             '2013-04-22' => '2013-04-22',
310             '2013/04/22' => '2013-04-22',
311             '2013.04.22' => '2013-04-22',
312             '22-04-2013' => '2013-04-22',
313             '22/04/2013' => '2013-04-22',
314             '22.04.2013' => '2013-04-22',
315             '04/22/2013' => '2013-04-22',
316             '22.4.2013'  => '2013-04-22',
317             '20130422'   => '2013-04-22',
318             '1900-10-10' => '1900-10-10',
319             '01-01-1900' => '1900-01-01',
320             '01/30/1960' => '1960-01-30'
321         );
322
323         foreach ($test as $datetime => $ts) {
324             $result = rcube_utils::anytodatetime($datetime);
09c58d 325             $this->assertSame($ts, $result ? $result->format('Y-m-d') : false, "Error parsing date: $datetime");
TB 326         }
327     }
328
329     /**
330      * rcube:utils::anytodatetime()
331      */
332     function test_anytodatetime_timezone()
333     {
334         $tz = new DateTimeZone('Europe/Helsinki');
335         $test = array(
336             'Jan 1st 2014 +0800' => '2013-12-31 18:00',  // result in target timezone
337             'Jan 1st 14 45:42'   => '2014-01-01 00:00',  // force fallback to rcube_utils::strtotime()
338             'Jan 1st 2014 UK'    => '2014-01-01 00:00',
339             'Invalid date'       => false,
340         );
341
342         foreach ($test as $datetime => $ts) {
343             $result = rcube_utils::anytodatetime($datetime, $tz);
344             if ($result) $result->setTimezone($tz);  // move to target timezone for comparison
345             $this->assertSame($ts, $result ? $result->format('Y-m-d H:i') : false, "Error parsing date: $datetime");
b1f3c3 346         }
AM 347     }
348
349     /**
e8b82c 350      * rcube:utils::tokenize_string()
TB 351      */
352     function test_tokenize_string()
353     {
354         $test = array(
355             ''        => array(),
356             'abc d'   => array('abc'),
357             'abc de'  => array('abc','de'),
358             'äàé;êöü-xyz' => array('äàé','êöü','xyz'),
359             '日期格式' => array('日期格式'),
360         );
361
362         foreach ($test as $input => $output) {
363             $result = rcube_utils::tokenize_string($input);
364             $this->assertSame($output, $result);
365         }
366     }
367
368     /**
517c9f 369      * rcube:utils::normalize_string()
896e2b 370      */
AM 371     function test_normalize_string()
372     {
373         $test = array(
d92158 374             ''        => '',
896e2b 375             'abc def' => 'abc def',
49dad5 376             'ÇçäâàåæéêëèïîìÅÉöôòüûùÿøØáíóúñÑÁÂÀãÃÊËÈÍÎÏÓÔõÕÚÛÙýÝ' => 'ccaaaaaeeeeiiiaeooouuuyooaiounnaaaaaeeeiiioooouuuyy',
AM 377             'ąáâäćçčéęëěíîłľĺńňóôöŕřśšşťţůúűüźžżýĄŚŻŹĆ' => 'aaaaccceeeeiilllnnooorrsssttuuuuzzzyaszzc',
e8b82c 378             'ßs'  => 'sss',
TB 379             'Xae' => 'xa',
380             'Xoe' => 'xo',
381             'Xue' => 'xu',
382             '项目' => '项目',
896e2b 383         );
AM 384
884070 385         // this test fails on PHP 5.3.3
AM 386         if (PHP_VERSION_ID > 50303) {
387             $test['ß']  = '';
388             $test['日'] = '';
389         }
390
896e2b 391         foreach ($test as $input => $output) {
AM 392             $result = rcube_utils::normalize_string($input);
e8b82c 393             $this->assertSame($output, $result, "Error normalizing '$input'");
896e2b 394         }
AM 395     }
517c9f 396
AM 397     /**
5143c4 398      * rcube:utils::words_match()
AM 399      */
400     function test_words_match()
401     {
402         $test = array(
403             array('', 'test', false),
404             array('test', 'test', true),
405             array('test', 'none', false),
406             array('test', 'test xyz', false),
407             array('test xyz', 'test xyz', true),
408             array('this is test', 'test', true),
409             // try some binary content
410             array('this is test ' . base64_decode('R0lGODlhDwAPAIAAAMDAwAAAACH5BAEAAAAALAAAAAAPAA8AQAINhI+py+0Po5y02otnAQA7'), 'test', true),
411             array('this is test ' . base64_decode('R0lGODlhDwAPAIAAAMDAwAAAACH5BAEAAAAALAAAAAAPAA8AQAINhI+py+0Po5y02otnAQA7'), 'none', false),
412         );
413
414         foreach ($test as $idx => $params) {
415             $result = rcube_utils::words_match($params[0], $params[1]);
416             $this->assertSame($params[2], $result, "words_match() at index $idx");
417         }
418     }
419
420     /**
517c9f 421      * rcube:utils::is_absolute_path()
AM 422      */
423     function test_is_absolute_path()
424     {
425         if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
426             $test = array(
427                 '' => false,
428                 "C:\\" => true,
429                 'some/path' => false,
430             );
431         }
432         else {
433             $test = array(
434                 '' => false,
435                 '/path' => true,
436                 'some/path' => false,
437             );
438         }
439
440         foreach ($test as $input => $output) {
441             $result = rcube_utils::is_absolute_path($input);
442             $this->assertSame($output, $result);
443         }
444     }
5529d9 445
AM 446     /**
447      * rcube:utils::random_bytes()
448      */
449     function test_random_bytes()
450     {
451         $this->assertSame(15, strlen(rcube_utils::random_bytes(15)));
452         $this->assertSame(1, strlen(rcube_utils::random_bytes(1)));
453         $this->assertSame(0, strlen(rcube_utils::random_bytes(0)));
454         $this->assertSame(0, strlen(rcube_utils::random_bytes(-1)));
455     }
c83b83 456 }