See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Unit tests for /lib/filelib.php. 19 * 20 * @package core 21 * @category test 22 * @copyright 2009 Jerome Mouneyrac 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 namespace core; 27 28 use core_filetypes; 29 use curl; 30 use repository; 31 32 defined('MOODLE_INTERNAL') || die(); 33 34 global $CFG; 35 require_once($CFG->libdir . '/filelib.php'); 36 require_once($CFG->dirroot . '/repository/lib.php'); 37 38 /** 39 * Unit tests for /lib/filelib.php. 40 * 41 * @package core 42 * @category test 43 * @copyright 2009 Jerome Mouneyrac 44 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 45 */ 46 class filelib_test extends \advanced_testcase { 47 public function test_format_postdata_for_curlcall() { 48 49 // POST params with just simple types. 50 $postdatatoconvert = array( 'userid' => 1, 'roleid' => 22, 'name' => 'john'); 51 $expectedresult = "userid=1&roleid=22&name=john"; 52 $postdata = format_postdata_for_curlcall($postdatatoconvert); 53 $this->assertEquals($expectedresult, $postdata); 54 55 // POST params with a string containing & character. 56 $postdatatoconvert = array( 'name' => 'john&emilie', 'roleid' => 22); 57 $expectedresult = "name=john%26emilie&roleid=22"; // Urlencode: '%26' => '&'. 58 $postdata = format_postdata_for_curlcall($postdatatoconvert); 59 $this->assertEquals($expectedresult, $postdata); 60 61 // POST params with an empty value. 62 $postdatatoconvert = array( 'name' => null, 'roleid' => 22); 63 $expectedresult = "name=&roleid=22"; 64 $postdata = format_postdata_for_curlcall($postdatatoconvert); 65 $this->assertEquals($expectedresult, $postdata); 66 67 // POST params with complex types. 68 $postdatatoconvert = array( 'users' => array( 69 array( 70 'id' => 2, 71 'customfields' => array( 72 array 73 ( 74 'type' => 'Color', 75 'value' => 'violet' 76 ) 77 ) 78 ) 79 ) 80 ); 81 $expectedresult = "users[0][id]=2&users[0][customfields][0][type]=Color&users[0][customfields][0][value]=violet"; 82 $postdata = format_postdata_for_curlcall($postdatatoconvert); 83 $this->assertEquals($expectedresult, $postdata); 84 85 // POST params with other complex types. 86 $postdatatoconvert = array ('members' => 87 array( 88 array('groupid' => 1, 'userid' => 1) 89 , array('groupid' => 1, 'userid' => 2) 90 ) 91 ); 92 $expectedresult = "members[0][groupid]=1&members[0][userid]=1&members[1][groupid]=1&members[1][userid]=2"; 93 $postdata = format_postdata_for_curlcall($postdatatoconvert); 94 $this->assertEquals($expectedresult, $postdata); 95 } 96 97 public function test_download_file_content() { 98 global $CFG; 99 100 // Test http success first. 101 $testhtml = $this->getExternalTestFileUrl('/test.html'); 102 103 $contents = download_file_content($testhtml); 104 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents)); 105 106 $tofile = "$CFG->tempdir/test.html"; 107 @unlink($tofile); 108 $result = download_file_content($testhtml, null, null, false, 300, 20, false, $tofile); 109 $this->assertTrue($result); 110 $this->assertFileExists($tofile); 111 $this->assertSame(file_get_contents($tofile), $contents); 112 @unlink($tofile); 113 114 $result = download_file_content($testhtml, null, null, false, 300, 20, false, null, true); 115 $this->assertSame($contents, $result); 116 117 $response = download_file_content($testhtml, null, null, true); 118 $this->assertInstanceOf('stdClass', $response); 119 $this->assertSame('200', $response->status); 120 $this->assertTrue(is_array($response->headers)); 121 $this->assertMatchesRegularExpression('|^HTTP/1\.[01] 200 OK$|', rtrim($response->response_code)); 122 $this->assertSame($contents, $response->results); 123 $this->assertSame('', $response->error); 124 125 // Test https success. 126 $testhtml = $this->getExternalTestFileUrl('/test.html', true); 127 128 $contents = download_file_content($testhtml, null, null, false, 300, 20, true); 129 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents)); 130 131 $contents = download_file_content($testhtml); 132 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents)); 133 134 // Now 404. 135 $testhtml = $this->getExternalTestFileUrl('/test.html_nonexistent'); 136 137 $contents = download_file_content($testhtml); 138 $this->assertFalse($contents); 139 $this->assertDebuggingCalled(); 140 141 $response = download_file_content($testhtml, null, null, true); 142 $this->assertInstanceOf('stdClass', $response); 143 $this->assertSame('404', $response->status); 144 $this->assertTrue(is_array($response->headers)); 145 $this->assertMatchesRegularExpression('|^HTTP/1\.[01] 404 Not Found$|', rtrim($response->response_code)); 146 // Do not test the response starts with DOCTYPE here because some servers may return different headers. 147 $this->assertSame('', $response->error); 148 149 // Invalid url. 150 $testhtml = $this->getExternalTestFileUrl('/test.html'); 151 $testhtml = str_replace('http://', 'ftp://', $testhtml); 152 153 $contents = download_file_content($testhtml); 154 $this->assertFalse($contents); 155 156 // Test standard redirects. 157 $testurl = $this->getExternalTestFileUrl('/test_redir.php'); 158 159 $contents = download_file_content("$testurl?redir=2"); 160 $this->assertSame('done', $contents); 161 162 $contents = download_file_content("$testurl?redir=2&verbose=1"); 163 $this->assertSame('done', $contents); 164 165 $response = download_file_content("$testurl?redir=2", null, null, true); 166 $this->assertInstanceOf('stdClass', $response); 167 $this->assertSame('200', $response->status); 168 $this->assertTrue(is_array($response->headers)); 169 $this->assertMatchesRegularExpression('|^HTTP/1\.[01] 200 OK$|', rtrim($response->response_code)); 170 $this->assertSame('done', $response->results); 171 $this->assertSame('', $response->error); 172 173 $response = download_file_content("$testurl?redir=2&verbose=1", null, null, true); 174 $this->assertInstanceOf('stdClass', $response); 175 $this->assertSame('200', $response->status); 176 $this->assertTrue(is_array($response->headers)); 177 $this->assertMatchesRegularExpression('|^HTTP/1\.[01] 200 OK$|', rtrim($response->response_code)); 178 $this->assertSame('done', $response->results); 179 $this->assertSame('', $response->error); 180 181 // Commented out this block if there are performance problems. 182 /* 183 $contents = download_file_content("$testurl?redir=6"); 184 $this->assertFalse(false, $contents); 185 $this->assertDebuggingCalled(); 186 $response = download_file_content("$testurl?redir=6", null, null, true); 187 $this->assertInstanceOf('stdClass', $response); 188 $this->assertSame('0', $response->status); 189 $this->assertTrue(is_array($response->headers)); 190 $this->assertFalse($response->results); 191 $this->assertNotEmpty($response->error); 192 */ 193 194 // Test relative redirects. 195 $testurl = $this->getExternalTestFileUrl('/test_relative_redir.php'); 196 197 $contents = download_file_content("$testurl"); 198 $this->assertSame('done', $contents); 199 200 $contents = download_file_content("$testurl?unused=xxx"); 201 $this->assertSame('done', $contents); 202 } 203 204 /** 205 * Test curl basics. 206 */ 207 public function test_curl_basics() { 208 global $CFG; 209 210 // Test HTTP success. 211 $testhtml = $this->getExternalTestFileUrl('/test.html'); 212 213 $curl = new \curl(); 214 $contents = $curl->get($testhtml); 215 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents)); 216 $this->assertSame(0, $curl->get_errno()); 217 218 $curl = new \curl(); 219 $tofile = "$CFG->tempdir/test.html"; 220 @unlink($tofile); 221 $fp = fopen($tofile, 'w'); 222 $result = $curl->get($testhtml, array(), array('CURLOPT_FILE'=>$fp)); 223 $this->assertTrue($result); 224 fclose($fp); 225 $this->assertFileExists($tofile); 226 $this->assertSame($contents, file_get_contents($tofile)); 227 @unlink($tofile); 228 229 $curl = new \curl(); 230 $tofile = "$CFG->tempdir/test.html"; 231 @unlink($tofile); 232 $result = $curl->download_one($testhtml, array(), array('filepath'=>$tofile)); 233 $this->assertTrue($result); 234 $this->assertFileExists($tofile); 235 $this->assertSame($contents, file_get_contents($tofile)); 236 @unlink($tofile); 237 238 // Test 404 request. 239 $curl = new \curl(); 240 $contents = $curl->get($this->getExternalTestFileUrl('/i.do.not.exist')); 241 $response = $curl->getResponse(); 242 $this->assertSame('404 Not Found', reset($response)); 243 $this->assertSame(0, $curl->get_errno()); 244 } 245 246 /** 247 * Test a curl basic request with security enabled. 248 */ 249 public function test_curl_basics_with_security_helper() { 250 $this->resetAfterTest(); 251 252 // Test a request with a basic hostname filter applied. 253 $testhtml = $this->getExternalTestFileUrl('/test.html'); 254 $url = new \moodle_url($testhtml); 255 $host = $url->get_host(); 256 set_config('curlsecurityblockedhosts', $host); // Blocks $host. 257 258 // Create curl with the default security enabled. We expect this to be blocked. 259 $curl = new \curl(); 260 $contents = $curl->get($testhtml); 261 $expected = $curl->get_security()->get_blocked_url_string(); 262 $this->assertSame($expected, $contents); 263 $this->assertSame(0, $curl->get_errno()); 264 265 // Now, create a curl using the 'ignoresecurity' override. 266 // We expect this request to pass, despite the admin setting having been set earlier. 267 $curl = new \curl(['ignoresecurity' => true]); 268 $contents = $curl->get($testhtml); 269 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents)); 270 $this->assertSame(0, $curl->get_errno()); 271 272 // Now, try injecting a mock security helper into curl. This will override the default helper. 273 $mockhelper = $this->getMockBuilder('\core\files\curl_security_helper')->getMock(); 274 275 // Make the mock return a different string. 276 $mockhelper->expects($this->any())->method('get_blocked_url_string')->will($this->returnValue('You shall not pass')); 277 278 // And make the mock security helper block all URLs. This helper instance doesn't care about config. 279 $mockhelper->expects($this->any())->method('url_is_blocked')->will($this->returnValue(true)); 280 281 $curl = new \curl(['securityhelper' => $mockhelper]); 282 $contents = $curl->get($testhtml); 283 $this->assertSame('You shall not pass', $curl->get_security()->get_blocked_url_string()); 284 $this->assertSame($curl->get_security()->get_blocked_url_string(), $contents); 285 } 286 287 public function test_curl_redirects() { 288 global $CFG; 289 290 $testurl = $this->getExternalTestFileUrl('/test_redir.php'); 291 292 $curl = new \curl(); 293 $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>2)); 294 $response = $curl->getResponse(); 295 $this->assertSame('200 OK', reset($response)); 296 $this->assertSame(0, $curl->get_errno()); 297 $this->assertSame(2, $curl->info['redirect_count']); 298 $this->assertSame('done', $contents); 299 300 // All redirects are emulated now. Enabling "emulateredirects" explicitly does not have effect. 301 $curl = new \curl(); 302 $curl->emulateredirects = true; 303 $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>2)); 304 $response = $curl->getResponse(); 305 $this->assertSame('200 OK', reset($response)); 306 $this->assertSame(0, $curl->get_errno()); 307 $this->assertSame(2, $curl->info['redirect_count']); 308 $this->assertSame('done', $contents); 309 310 // All redirects are emulated now. Attempting to disable "emulateredirects" explicitly causes warning. 311 $curl = new \curl(); 312 $curl->emulateredirects = false; 313 $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS' => 2)); 314 $response = $curl->getResponse(); 315 $this->assertDebuggingCalled('Attempting to disable emulated redirects has no effect any more!'); 316 $this->assertSame('200 OK', reset($response)); 317 $this->assertSame(0, $curl->get_errno()); 318 $this->assertSame(2, $curl->info['redirect_count']); 319 $this->assertSame('done', $contents); 320 321 // This test was failing for people behind Squid proxies. Squid does not 322 // fully support HTTP 1.1, so converts things to HTTP 1.0, where the name 323 // of the status code is different. 324 reset($response); 325 if (key($response) === 'HTTP/1.0') { 326 $responsecode302 = '302 Moved Temporarily'; 327 } else { 328 $responsecode302 = '302 Found'; 329 } 330 331 $curl = new \curl(); 332 $contents = $curl->get("$testurl?redir=3", array(), array('CURLOPT_FOLLOWLOCATION'=>0)); 333 $response = $curl->getResponse(); 334 $this->assertSame($responsecode302, reset($response)); 335 $this->assertSame(0, $curl->get_errno()); 336 $this->assertSame(302, $curl->info['http_code']); 337 $this->assertSame('', $contents); 338 339 $curl = new \curl(); 340 $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>1)); 341 $this->assertSame(CURLE_TOO_MANY_REDIRECTS, $curl->get_errno()); 342 $this->assertNotEmpty($contents); 343 344 $curl = new \curl(); 345 $tofile = "$CFG->tempdir/test.html"; 346 @unlink($tofile); 347 $fp = fopen($tofile, 'w'); 348 $result = $curl->get("$testurl?redir=1", array(), array('CURLOPT_FILE'=>$fp)); 349 $this->assertTrue($result); 350 fclose($fp); 351 $this->assertFileExists($tofile); 352 $this->assertSame('done', file_get_contents($tofile)); 353 @unlink($tofile); 354 355 $curl = new \curl(); 356 $tofile = "$CFG->tempdir/test.html"; 357 @unlink($tofile); 358 $fp = fopen($tofile, 'w'); 359 $result = $curl->get("$testurl?redir=1&verbose=1", array(), array('CURLOPT_FILE' => $fp)); 360 $this->assertTrue($result); 361 fclose($fp); 362 $this->assertFileExists($tofile); 363 $this->assertSame('done', file_get_contents($tofile)); 364 @unlink($tofile); 365 366 $curl = new \curl(); 367 $tofile = "$CFG->tempdir/test.html"; 368 @unlink($tofile); 369 $result = $curl->download_one("$testurl?redir=1", array(), array('filepath'=>$tofile)); 370 $this->assertTrue($result); 371 $this->assertFileExists($tofile); 372 $this->assertSame('done', file_get_contents($tofile)); 373 @unlink($tofile); 374 375 $curl = new \curl(); 376 $tofile = "$CFG->tempdir/test.html"; 377 @unlink($tofile); 378 $result = $curl->download_one("$testurl?redir=1&verbose=1", array(), array('filepath' => $tofile)); 379 $this->assertTrue($result); 380 $this->assertFileExists($tofile); 381 $this->assertSame('done', file_get_contents($tofile)); 382 @unlink($tofile); 383 } 384 385 /** 386 * Test that redirects to blocked hosts are blocked. 387 */ 388 public function test_curl_blocked_redirect() { 389 $this->resetAfterTest(); 390 391 $testurl = $this->getExternalTestFileUrl('/test_redir.php'); 392 393 // Block a host. 394 // Note: moodle.com is the URL redirected to when test_redir.php has the param extdest=1 set. 395 set_config('curlsecurityblockedhosts', 'moodle.com'); 396 397 // Redirecting to a non-blocked host should resolve. 398 $curl = new \curl(); 399 $contents = $curl->get("{$testurl}?redir=2"); 400 $response = $curl->getResponse(); 401 $this->assertSame('200 OK', reset($response)); 402 $this->assertSame(0, $curl->get_errno()); 403 404 // Redirecting to the blocked host should fail. 405 $curl = new \curl(); 406 $blockedstring = $curl->get_security()->get_blocked_url_string(); 407 $contents = $curl->get("{$testurl}?redir=1&extdest=1"); 408 $this->assertSame($blockedstring, $contents); 409 $this->assertSame(0, $curl->get_errno()); 410 411 // Redirecting to the blocked host after multiple successful redirects should also fail. 412 $curl = new \curl(); 413 $contents = $curl->get("{$testurl}?redir=3&extdest=1"); 414 $this->assertSame($blockedstring, $contents); 415 $this->assertSame(0, $curl->get_errno()); 416 } 417 418 public function test_curl_relative_redirects() { 419 // Test relative location redirects. 420 $testurl = $this->getExternalTestFileUrl('/test_relative_redir.php'); 421 422 $curl = new \curl(); 423 $contents = $curl->get($testurl); 424 $response = $curl->getResponse(); 425 $this->assertSame('200 OK', reset($response)); 426 $this->assertSame(0, $curl->get_errno()); 427 $this->assertSame(1, $curl->info['redirect_count']); 428 $this->assertSame('done', $contents); 429 430 // Test different redirect types. 431 $testurl = $this->getExternalTestFileUrl('/test_relative_redir.php'); 432 433 $curl = new \curl(); 434 $contents = $curl->get("$testurl?type=301"); 435 $response = $curl->getResponse(); 436 $this->assertSame('200 OK', reset($response)); 437 $this->assertSame(0, $curl->get_errno()); 438 $this->assertSame(1, $curl->info['redirect_count']); 439 $this->assertSame('done', $contents); 440 441 $curl = new \curl(); 442 $contents = $curl->get("$testurl?type=302"); 443 $response = $curl->getResponse(); 444 $this->assertSame('200 OK', reset($response)); 445 $this->assertSame(0, $curl->get_errno()); 446 $this->assertSame(1, $curl->info['redirect_count']); 447 $this->assertSame('done', $contents); 448 449 $curl = new \curl(); 450 $contents = $curl->get("$testurl?type=303"); 451 $response = $curl->getResponse(); 452 $this->assertSame('200 OK', reset($response)); 453 $this->assertSame(0, $curl->get_errno()); 454 $this->assertSame(1, $curl->info['redirect_count']); 455 $this->assertSame('done', $contents); 456 457 $curl = new \curl(); 458 $contents = $curl->get("$testurl?type=307"); 459 $response = $curl->getResponse(); 460 $this->assertSame('200 OK', reset($response)); 461 $this->assertSame(0, $curl->get_errno()); 462 $this->assertSame(1, $curl->info['redirect_count']); 463 $this->assertSame('done', $contents); 464 465 $curl = new \curl(); 466 $contents = $curl->get("$testurl?type=308"); 467 $response = $curl->getResponse(); 468 $this->assertSame('200 OK', reset($response)); 469 $this->assertSame(0, $curl->get_errno()); 470 $this->assertSame(1, $curl->info['redirect_count']); 471 $this->assertSame('done', $contents); 472 } 473 474 public function test_curl_proxybypass() { 475 global $CFG; 476 $testurl = $this->getExternalTestFileUrl('/test.html'); 477 478 $oldproxy = $CFG->proxyhost; 479 $oldproxybypass = $CFG->proxybypass; 480 481 // Test without proxy bypass and inaccessible proxy. 482 $CFG->proxyhost = 'i.do.not.exist'; 483 $CFG->proxybypass = ''; 484 $curl = new \curl(); 485 $contents = $curl->get($testurl); 486 $this->assertNotEquals(0, $curl->get_errno()); 487 $this->assertNotEquals('47250a973d1b88d9445f94db4ef2c97a', md5($contents)); 488 489 // Test with proxy bypass. 490 $testurlhost = parse_url($testurl, PHP_URL_HOST); 491 $CFG->proxybypass = $testurlhost; 492 $curl = new \curl(); 493 $contents = $curl->get($testurl); 494 $this->assertSame(0, $curl->get_errno()); 495 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents)); 496 497 $CFG->proxyhost = $oldproxy; 498 $CFG->proxybypass = $oldproxybypass; 499 } 500 501 /** 502 * Test that duplicate lines in the curl header are removed. 503 */ 504 public function test_duplicate_curl_header() { 505 $testurl = $this->getExternalTestFileUrl('/test_post.php'); 506 507 $curl = new \curl(); 508 $headerdata = 'Accept: application/json'; 509 $header = [$headerdata, $headerdata]; 510 $this->assertCount(2, $header); 511 $curl->setHeader($header); 512 $this->assertCount(1, $curl->header); 513 $this->assertEquals($headerdata, $curl->header[0]); 514 } 515 516 public function test_curl_post() { 517 $testurl = $this->getExternalTestFileUrl('/test_post.php'); 518 519 // Test post request. 520 $curl = new \curl(); 521 $contents = $curl->post($testurl, 'data=moodletest'); 522 $response = $curl->getResponse(); 523 $this->assertSame('200 OK', reset($response)); 524 $this->assertSame(0, $curl->get_errno()); 525 $this->assertSame('OK', $contents); 526 527 // Test 100 requests. 528 $curl = new \curl(); 529 $curl->setHeader('Expect: 100-continue'); 530 $contents = $curl->post($testurl, 'data=moodletest'); 531 $response = $curl->getResponse(); 532 $this->assertSame('200 OK', reset($response)); 533 $this->assertSame(0, $curl->get_errno()); 534 $this->assertSame('OK', $contents); 535 } 536 537 public function test_curl_file() { 538 $this->resetAfterTest(); 539 $testurl = $this->getExternalTestFileUrl('/test_file.php'); 540 541 $fs = get_file_storage(); 542 $filerecord = array( 543 'contextid' => \context_system::instance()->id, 544 'component' => 'test', 545 'filearea' => 'curl_post', 546 'itemid' => 0, 547 'filepath' => '/', 548 'filename' => 'test.txt' 549 ); 550 $teststring = 'moodletest'; 551 $testfile = $fs->create_file_from_string($filerecord, $teststring); 552 553 // Test post with file. 554 $data = array('testfile' => $testfile); 555 $curl = new \curl(); 556 $contents = $curl->post($testurl, $data); 557 $this->assertSame('OK', $contents); 558 } 559 560 public function test_curl_file_name() { 561 $this->resetAfterTest(); 562 $testurl = $this->getExternalTestFileUrl('/test_file_name.php'); 563 564 $fs = get_file_storage(); 565 $filerecord = array( 566 'contextid' => \context_system::instance()->id, 567 'component' => 'test', 568 'filearea' => 'curl_post', 569 'itemid' => 0, 570 'filepath' => '/', 571 'filename' => 'test.txt' 572 ); 573 $teststring = 'moodletest'; 574 $testfile = $fs->create_file_from_string($filerecord, $teststring); 575 576 // Test post with file. 577 $data = array('testfile' => $testfile); 578 $curl = new \curl(); 579 $contents = $curl->post($testurl, $data); 580 $this->assertSame('OK', $contents); 581 } 582 583 public function test_curl_protocols() { 584 585 // HTTP and HTTPS requests were verified in previous requests. Now check 586 // that we can selectively disable some protocols. 587 $curl = new \curl(); 588 589 // Other protocols than HTTP(S) are disabled by default. 590 $testurl = 'file:///'; 591 $curl->get($testurl); 592 $this->assertNotEmpty($curl->error); 593 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno); 594 595 $testurl = 'ftp://nowhere'; 596 $curl->get($testurl); 597 $this->assertNotEmpty($curl->error); 598 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno); 599 600 $testurl = 'telnet://somewhere'; 601 $curl->get($testurl); 602 $this->assertNotEmpty($curl->error); 603 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno); 604 605 // Protocols are also disabled during redirections. 606 $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php'); 607 $curl->get($testurl, array('proto' => 'file')); 608 $this->assertNotEmpty($curl->error); 609 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno); 610 611 $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php'); 612 $curl->get($testurl, array('proto' => 'ftp')); 613 $this->assertNotEmpty($curl->error); 614 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno); 615 616 $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php'); 617 $curl->get($testurl, array('proto' => 'telnet')); 618 $this->assertNotEmpty($curl->error); 619 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno); 620 } 621 622 /** 623 * Testing prepare draft area 624 * 625 * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org} 626 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 627 */ 628 public function test_prepare_draft_area() { 629 global $USER, $DB; 630 631 $this->resetAfterTest(true); 632 633 $generator = $this->getDataGenerator(); 634 $user = $generator->create_user(); 635 $usercontext = \context_user::instance($user->id); 636 $USER = $DB->get_record('user', array('id'=>$user->id)); 637 638 $repositorypluginname = 'user'; 639 640 $args = array(); 641 $args['type'] = $repositorypluginname; 642 $repos = repository::get_instances($args); 643 $userrepository = reset($repos); 644 $this->assertInstanceOf('repository', $userrepository); 645 646 $fs = get_file_storage(); 647 648 $syscontext = \context_system::instance(); 649 $component = 'core'; 650 $filearea = 'unittest'; 651 $itemid = 0; 652 $filepath = '/'; 653 $filename = 'test.txt'; 654 $sourcefield = 'Copyright stuff'; 655 656 $filerecord = array( 657 'contextid' => $syscontext->id, 658 'component' => $component, 659 'filearea' => $filearea, 660 'itemid' => $itemid, 661 'filepath' => $filepath, 662 'filename' => $filename, 663 'source' => $sourcefield, 664 ); 665 $ref = $fs->pack_reference($filerecord); 666 $originalfile = $fs->create_file_from_string($filerecord, 'Test content'); 667 $fileid = $originalfile->get_id(); 668 $this->assertInstanceOf('stored_file', $originalfile); 669 670 // Create a user private file. 671 $userfilerecord = new \stdClass; 672 $userfilerecord->contextid = $usercontext->id; 673 $userfilerecord->component = 'user'; 674 $userfilerecord->filearea = 'private'; 675 $userfilerecord->itemid = 0; 676 $userfilerecord->filepath = '/'; 677 $userfilerecord->filename = 'userfile.txt'; 678 $userfilerecord->source = 'test'; 679 $userfile = $fs->create_file_from_string($userfilerecord, 'User file content'); 680 $userfileref = $fs->pack_reference($userfilerecord); 681 682 $filerefrecord = clone((object)$filerecord); 683 $filerefrecord->filename = 'testref.txt'; 684 685 // Create a file reference. 686 $fileref = $fs->create_file_from_reference($filerefrecord, $userrepository->id, $userfileref); 687 $this->assertInstanceOf('stored_file', $fileref); 688 $this->assertEquals($userrepository->id, $fileref->get_repository_id()); 689 $this->assertSame($userfile->get_contenthash(), $fileref->get_contenthash()); 690 $this->assertEquals($userfile->get_filesize(), $fileref->get_filesize()); 691 $this->assertMatchesRegularExpression('#' . $userfile->get_filename(). '$#', $fileref->get_reference_details()); 692 693 $draftitemid = 0; 694 file_prepare_draft_area($draftitemid, $syscontext->id, $component, $filearea, $itemid); 695 696 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid); 697 $this->assertCount(3, $draftfiles); 698 699 $draftfile = $fs->get_file($usercontext->id, 'user', 'draft', $draftitemid, $filepath, $filename); 700 $source = unserialize($draftfile->get_source()); 701 $this->assertSame($ref, $source->original); 702 $this->assertSame($sourcefield, $source->source); 703 704 $draftfileref = $fs->get_file($usercontext->id, 'user', 'draft', $draftitemid, $filepath, $filerefrecord->filename); 705 $this->assertInstanceOf('stored_file', $draftfileref); 706 $this->assertTrue($draftfileref->is_external_file()); 707 708 // Change some information. 709 $author = 'Dongsheng Cai'; 710 $draftfile->set_author($author); 711 $newsourcefield = 'Get from Flickr'; 712 $license = 'GPLv3'; 713 $draftfile->set_license($license); 714 // If you want to really just change source field, do this. 715 $source = unserialize($draftfile->get_source()); 716 $newsourcefield = 'From flickr'; 717 $source->source = $newsourcefield; 718 $draftfile->set_source(serialize($source)); 719 720 // Save changed file. 721 file_save_draft_area_files($draftitemid, $syscontext->id, $component, $filearea, $itemid); 722 723 $file = $fs->get_file($syscontext->id, $component, $filearea, $itemid, $filepath, $filename); 724 725 // Make sure it's the original file id. 726 $this->assertEquals($fileid, $file->get_id()); 727 $this->assertInstanceOf('stored_file', $file); 728 $this->assertSame($author, $file->get_author()); 729 $this->assertSame($license, $file->get_license()); 730 $this->assertEquals($newsourcefield, $file->get_source()); 731 } 732 733 /** 734 * Testing deleting original files. 735 * 736 * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org} 737 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 738 */ 739 public function test_delete_original_file_from_draft() { 740 global $USER, $DB; 741 742 $this->resetAfterTest(true); 743 744 $generator = $this->getDataGenerator(); 745 $user = $generator->create_user(); 746 $usercontext = \context_user::instance($user->id); 747 $USER = $DB->get_record('user', array('id'=>$user->id)); 748 749 $repositorypluginname = 'user'; 750 751 $args = array(); 752 $args['type'] = $repositorypluginname; 753 $repos = repository::get_instances($args); 754 $userrepository = reset($repos); 755 $this->assertInstanceOf('repository', $userrepository); 756 757 $fs = get_file_storage(); 758 $syscontext = \context_system::instance(); 759 760 $filecontent = 'User file content'; 761 762 // Create a user private file. 763 $userfilerecord = new \stdClass; 764 $userfilerecord->contextid = $usercontext->id; 765 $userfilerecord->component = 'user'; 766 $userfilerecord->filearea = 'private'; 767 $userfilerecord->itemid = 0; 768 $userfilerecord->filepath = '/'; 769 $userfilerecord->filename = 'userfile.txt'; 770 $userfilerecord->source = 'test'; 771 $userfile = $fs->create_file_from_string($userfilerecord, $filecontent); 772 $userfileref = $fs->pack_reference($userfilerecord); 773 $contenthash = $userfile->get_contenthash(); 774 775 $filerecord = array( 776 'contextid' => $syscontext->id, 777 'component' => 'core', 778 'filearea' => 'phpunit', 779 'itemid' => 0, 780 'filepath' => '/', 781 'filename' => 'test.txt', 782 ); 783 // Create a file reference. 784 $fileref = $fs->create_file_from_reference($filerecord, $userrepository->id, $userfileref); 785 $this->assertInstanceOf('stored_file', $fileref); 786 $this->assertEquals($userrepository->id, $fileref->get_repository_id()); 787 $this->assertSame($userfile->get_contenthash(), $fileref->get_contenthash()); 788 $this->assertEquals($userfile->get_filesize(), $fileref->get_filesize()); 789 $this->assertMatchesRegularExpression('#' . $userfile->get_filename(). '$#', $fileref->get_reference_details()); 790 791 $draftitemid = 0; 792 file_prepare_draft_area($draftitemid, $usercontext->id, 'user', 'private', 0); 793 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid); 794 $this->assertCount(2, $draftfiles); 795 $draftfile = $fs->get_file($usercontext->id, 'user', 'draft', $draftitemid, $userfilerecord->filepath, $userfilerecord->filename); 796 $draftfile->delete(); 797 // Save changed file. 798 file_save_draft_area_files($draftitemid, $usercontext->id, 'user', 'private', 0); 799 800 // The file reference should be a regular moodle file now. 801 $fileref = $fs->get_file($syscontext->id, 'core', 'phpunit', 0, '/', 'test.txt'); 802 $this->assertFalse($fileref->is_external_file()); 803 $this->assertSame($contenthash, $fileref->get_contenthash()); 804 $this->assertEquals($filecontent, $fileref->get_content()); 805 } 806 807 /** 808 * Test avoid file merging when working with draft areas. 809 */ 810 public function test_ignore_file_merging_in_draft_area() { 811 global $USER, $DB; 812 813 $this->resetAfterTest(true); 814 815 $generator = $this->getDataGenerator(); 816 $user = $generator->create_user(); 817 $usercontext = \context_user::instance($user->id); 818 $USER = $DB->get_record('user', array('id' => $user->id)); 819 820 $repositorypluginname = 'user'; 821 822 $args = array(); 823 $args['type'] = $repositorypluginname; 824 $repos = repository::get_instances($args); 825 $userrepository = reset($repos); 826 $this->assertInstanceOf('repository', $userrepository); 827 828 $fs = get_file_storage(); 829 $syscontext = \context_system::instance(); 830 831 $filecontent = 'User file content'; 832 833 // Create a user private file. 834 $userfilerecord = new \stdClass; 835 $userfilerecord->contextid = $usercontext->id; 836 $userfilerecord->component = 'user'; 837 $userfilerecord->filearea = 'private'; 838 $userfilerecord->itemid = 0; 839 $userfilerecord->filepath = '/'; 840 $userfilerecord->filename = 'userfile.txt'; 841 $userfilerecord->source = 'test'; 842 $userfile = $fs->create_file_from_string($userfilerecord, $filecontent); 843 $userfileref = $fs->pack_reference($userfilerecord); 844 $contenthash = $userfile->get_contenthash(); 845 846 $filerecord = array( 847 'contextid' => $syscontext->id, 848 'component' => 'core', 849 'filearea' => 'phpunit', 850 'itemid' => 0, 851 'filepath' => '/', 852 'filename' => 'test.txt', 853 ); 854 // Create a file reference. 855 $fileref = $fs->create_file_from_reference($filerecord, $userrepository->id, $userfileref); 856 $this->assertCount(2, $fs->get_area_files($usercontext->id, 'user', 'private')); // 2 because includes the '.' file. 857 858 // Save using empty draft item id, all files will be deleted. 859 file_save_draft_area_files(0, $usercontext->id, 'user', 'private', 0); 860 $this->assertCount(0, $fs->get_area_files($usercontext->id, 'user', 'private')); 861 862 // Create a file again. 863 $userfile = $fs->create_file_from_string($userfilerecord, $filecontent); 864 $this->assertCount(2, $fs->get_area_files($usercontext->id, 'user', 'private')); 865 866 // Save without merge. 867 file_save_draft_area_files(IGNORE_FILE_MERGE, $usercontext->id, 'user', 'private', 0); 868 $this->assertCount(2, $fs->get_area_files($usercontext->id, 'user', 'private')); 869 // Save again, this time including some inline text. 870 $inlinetext = 'Some text <img src="@@PLUGINFILE@@/file.png">'; 871 $text = file_save_draft_area_files(IGNORE_FILE_MERGE, $usercontext->id, 'user', 'private', 0, null, $inlinetext); 872 $this->assertCount(2, $fs->get_area_files($usercontext->id, 'user', 'private')); 873 $this->assertEquals($inlinetext, $text); 874 } 875 876 /** 877 * Testing deleting file_save_draft_area_files won't accidentally wipe unintended files. 878 */ 879 public function test_file_save_draft_area_files_itemid_cannot_be_false() { 880 global $USER, $DB; 881 $this->resetAfterTest(); 882 883 $generator = $this->getDataGenerator(); 884 $user = $generator->create_user(); 885 $usercontext = \context_user::instance($user->id); 886 $USER = $DB->get_record('user', ['id' => $user->id]); 887 888 $draftitemid = 0; 889 file_prepare_draft_area($draftitemid, $usercontext->id, 'user', 'private', 0); 890 891 // Call file_save_draft_area_files with itemid false - which could only happen due to a bug. 892 // This should throw an exception. 893 $this->expectExceptionMessage('file_save_draft_area_files was called with $itemid false. ' . 894 'This suggests a bug, because it would wipe all (' . $usercontext->id . ', user, private) files.'); 895 file_save_draft_area_files($draftitemid, $usercontext->id, 'user', 'private', false); 896 } 897 898 /** 899 * Tests the strip_double_headers function in the curl class. 900 */ 901 public function test_curl_strip_double_headers() { 902 // Example from issue tracker. 903 $mdl30648example = <<<EOF 904 HTTP/1.0 407 Proxy Authentication Required 905 Server: squid/2.7.STABLE9 906 Date: Thu, 08 Dec 2011 14:44:33 GMT 907 Content-Type: text/html 908 Content-Length: 1275 909 X-Squid-Error: ERR_CACHE_ACCESS_DENIED 0 910 Proxy-Authenticate: Basic realm="Squid proxy-caching web server" 911 X-Cache: MISS from homer.lancs.ac.uk 912 X-Cache-Lookup: NONE from homer.lancs.ac.uk:3128 913 Via: 1.0 homer.lancs.ac.uk:3128 (squid/2.7.STABLE9) 914 Connection: close 915 916 HTTP/1.0 200 OK 917 Server: Apache 918 X-Lb-Nocache: true 919 Cache-Control: private, max-age=15, no-transform 920 ETag: "4d69af5d8ba873ea9192c489e151bd7b" 921 Content-Type: text/html 922 Date: Thu, 08 Dec 2011 14:44:53 GMT 923 Set-Cookie: BBC-UID=c4de2e109c8df6a51de627cee11b214bd4fb6054a030222488317afb31b343360MoodleBot/1.0; expires=Mon, 07-Dec-15 14:44:53 GMT; path=/; domain=bbc.co.uk 924 X-Cache-Action: MISS 925 X-Cache-Age: 0 926 Vary: Cookie,X-Country,X-Ip-is-uk-combined,X-Ip-is-advertise-combined,X-Ip_is_uk_combined,X-Ip_is_advertise_combined, X-GeoIP 927 X-Cache: MISS from ww 928 929 <html>... 930 EOF; 931 $mdl30648expected = <<<EOF 932 HTTP/1.0 200 OK 933 Server: Apache 934 X-Lb-Nocache: true 935 Cache-Control: private, max-age=15, no-transform 936 ETag: "4d69af5d8ba873ea9192c489e151bd7b" 937 Content-Type: text/html 938 Date: Thu, 08 Dec 2011 14:44:53 GMT 939 Set-Cookie: BBC-UID=c4de2e109c8df6a51de627cee11b214bd4fb6054a030222488317afb31b343360MoodleBot/1.0; expires=Mon, 07-Dec-15 14:44:53 GMT; path=/; domain=bbc.co.uk 940 X-Cache-Action: MISS 941 X-Cache-Age: 0 942 Vary: Cookie,X-Country,X-Ip-is-uk-combined,X-Ip-is-advertise-combined,X-Ip_is_uk_combined,X-Ip_is_advertise_combined, X-GeoIP 943 X-Cache: MISS from ww 944 945 <html>... 946 EOF; 947 // For HTTP, replace the \n with \r\n. 948 $mdl30648example = preg_replace("~(?!<\r)\n~", "\r\n", $mdl30648example); 949 $mdl30648expected = preg_replace("~(?!<\r)\n~", "\r\n", $mdl30648expected); 950 951 // Test stripping works OK. 952 $this->assertSame($mdl30648expected, \curl::strip_double_headers($mdl30648example)); 953 // Test it does nothing to the 'plain' data. 954 $this->assertSame($mdl30648expected, \curl::strip_double_headers($mdl30648expected)); 955 956 // Example from OU proxy. 957 $httpsexample = <<<EOF 958 HTTP/1.0 200 Connection established 959 960 HTTP/1.1 200 OK 961 Date: Fri, 22 Feb 2013 17:14:23 GMT 962 Server: Apache/2 963 X-Powered-By: PHP/5.3.3-7+squeeze14 964 Content-Type: text/xml 965 Connection: close 966 Content-Encoding: gzip 967 Transfer-Encoding: chunked 968 969 <?xml version="1.0" encoding="ISO-8859-1" ?> 970 <rss version="2.0">... 971 EOF; 972 $httpsexpected = <<<EOF 973 HTTP/1.1 200 OK 974 Date: Fri, 22 Feb 2013 17:14:23 GMT 975 Server: Apache/2 976 X-Powered-By: PHP/5.3.3-7+squeeze14 977 Content-Type: text/xml 978 Connection: close 979 Content-Encoding: gzip 980 Transfer-Encoding: chunked 981 982 <?xml version="1.0" encoding="ISO-8859-1" ?> 983 <rss version="2.0">... 984 EOF; 985 // For HTTP, replace the \n with \r\n. 986 $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); 987 $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); 988 989 // Test stripping works OK. 990 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexample)); 991 // Test it does nothing to the 'plain' data. 992 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexpected)); 993 994 $httpsexample = <<<EOF 995 HTTP/1.0 200 Connection established 996 997 HTTP/2 200 OK 998 Date: Fri, 22 Feb 2013 17:14:23 GMT 999 Server: Apache/2 1000 X-Powered-By: PHP/5.3.3-7+squeeze14 1001 Content-Type: text/xml 1002 Connection: close 1003 Content-Encoding: gzip 1004 Transfer-Encoding: chunked 1005 1006 <?xml version="1.0" encoding="ISO-8859-1" ?> 1007 <rss version="2.0">... 1008 EOF; 1009 $httpsexpected = <<<EOF 1010 HTTP/2 200 OK 1011 Date: Fri, 22 Feb 2013 17:14:23 GMT 1012 Server: Apache/2 1013 X-Powered-By: PHP/5.3.3-7+squeeze14 1014 Content-Type: text/xml 1015 Connection: close 1016 Content-Encoding: gzip 1017 Transfer-Encoding: chunked 1018 1019 <?xml version="1.0" encoding="ISO-8859-1" ?> 1020 <rss version="2.0">... 1021 EOF; 1022 // For HTTP, replace the \n with \r\n. 1023 $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); 1024 $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); 1025 1026 // Test stripping works OK. 1027 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexample)); 1028 // Test it does nothing to the 'plain' data. 1029 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexpected)); 1030 1031 $httpsexample = <<<EOF 1032 HTTP/1.0 200 Connection established 1033 1034 HTTP/2.1 200 OK 1035 Date: Fri, 22 Feb 2013 17:14:23 GMT 1036 Server: Apache/2 1037 X-Powered-By: PHP/5.3.3-7+squeeze14 1038 Content-Type: text/xml 1039 Connection: close 1040 Content-Encoding: gzip 1041 Transfer-Encoding: chunked 1042 1043 <?xml version="1.0" encoding="ISO-8859-1" ?> 1044 <rss version="2.0">... 1045 EOF; 1046 $httpsexpected = <<<EOF 1047 HTTP/2.1 200 OK 1048 Date: Fri, 22 Feb 2013 17:14:23 GMT 1049 Server: Apache/2 1050 X-Powered-By: PHP/5.3.3-7+squeeze14 1051 Content-Type: text/xml 1052 Connection: close 1053 Content-Encoding: gzip 1054 Transfer-Encoding: chunked 1055 1056 <?xml version="1.0" encoding="ISO-8859-1" ?> 1057 <rss version="2.0">... 1058 EOF; 1059 // For HTTP, replace the \n with \r\n. 1060 $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); 1061 $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); 1062 1063 // Test stripping works OK. 1064 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexample)); 1065 // Test it does nothing to the 'plain' data. 1066 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexpected)); 1067 1068 $httpsexample = <<<EOF 1069 HTTP/1.1 200 Connection established 1070 1071 HTTP/3 200 OK 1072 Date: Fri, 22 Feb 2013 17:14:23 GMT 1073 Server: Apache/2 1074 X-Powered-By: PHP/5.3.3-7+squeeze14 1075 Content-Type: text/xml 1076 Connection: close 1077 Content-Encoding: gzip 1078 Transfer-Encoding: chunked 1079 1080 <?xml version="1.0" encoding="ISO-8859-1" ?> 1081 <rss version="2.0">... 1082 EOF; 1083 $httpsexpected = <<<EOF 1084 HTTP/3 200 OK 1085 Date: Fri, 22 Feb 2013 17:14:23 GMT 1086 Server: Apache/2 1087 X-Powered-By: PHP/5.3.3-7+squeeze14 1088 Content-Type: text/xml 1089 Connection: close 1090 Content-Encoding: gzip 1091 Transfer-Encoding: chunked 1092 1093 <?xml version="1.0" encoding="ISO-8859-1" ?> 1094 <rss version="2.0">... 1095 EOF; 1096 // For HTTP, replace the \n with \r\n. 1097 $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); 1098 $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); 1099 1100 // Test stripping works OK. 1101 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexample)); 1102 // Test it does nothing to the 'plain' data. 1103 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexpected)); 1104 1105 $httpsexample = <<<EOF 1106 HTTP/2 200 Connection established 1107 1108 HTTP/4 200 OK 1109 Date: Fri, 22 Feb 2013 17:14:23 GMT 1110 Server: Apache/2 1111 X-Powered-By: PHP/5.3.3-7+squeeze14 1112 Content-Type: text/xml 1113 Connection: close 1114 Content-Encoding: gzip 1115 Transfer-Encoding: chunked 1116 1117 <?xml version="1.0" encoding="ISO-8859-1" ?> 1118 <rss version="2.0">... 1119 EOF; 1120 $httpsexpected = <<<EOF 1121 HTTP/4 200 OK 1122 Date: Fri, 22 Feb 2013 17:14:23 GMT 1123 Server: Apache/2 1124 X-Powered-By: PHP/5.3.3-7+squeeze14 1125 Content-Type: text/xml 1126 Connection: close 1127 Content-Encoding: gzip 1128 Transfer-Encoding: chunked 1129 1130 <?xml version="1.0" encoding="ISO-8859-1" ?> 1131 <rss version="2.0">... 1132 EOF; 1133 // For HTTP, replace the \n with \r\n. 1134 $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); 1135 $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); 1136 1137 // Test stripping works OK. 1138 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexample)); 1139 // Test it does nothing to the 'plain' data. 1140 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexpected)); 1141 } 1142 1143 /** 1144 * Tests the get_mimetype_description function. 1145 */ 1146 public function test_get_mimetype_description() { 1147 $this->resetAfterTest(); 1148 1149 // Test example type (.doc). 1150 $this->assertEquals(get_string('application/msword', 'mimetypes'), 1151 get_mimetype_description(array('filename' => 'test.doc'))); 1152 1153 // Test an unknown file type. 1154 $this->assertEquals(get_string('document/unknown', 'mimetypes'), 1155 get_mimetype_description(array('filename' => 'test.frog'))); 1156 1157 // Test a custom filetype with no lang string specified. 1158 core_filetypes::add_type('frog', 'application/x-frog', 'document'); 1159 $this->assertEquals('application/x-frog', 1160 get_mimetype_description(array('filename' => 'test.frog'))); 1161 1162 // Test custom description. 1163 core_filetypes::update_type('frog', 'frog', 'application/x-frog', 'document', 1164 array(), '', 'Froggy file'); 1165 $this->assertEquals('Froggy file', 1166 get_mimetype_description(array('filename' => 'test.frog'))); 1167 1168 // Test custom description using multilang filter. 1169 \filter_manager::reset_caches(); 1170 filter_set_global_state('multilang', TEXTFILTER_ON); 1171 filter_set_applies_to_strings('multilang', true); 1172 core_filetypes::update_type('frog', 'frog', 'application/x-frog', 'document', 1173 array(), '', '<span lang="en" class="multilang">Green amphibian</span>' . 1174 '<span lang="fr" class="multilang">Amphibian vert</span>'); 1175 $this->assertEquals('Green amphibian', 1176 get_mimetype_description(array('filename' => 'test.frog'))); 1177 } 1178 1179 /** 1180 * Tests the get_mimetypes_array function. 1181 */ 1182 public function test_get_mimetypes_array() { 1183 $mimeinfo = get_mimetypes_array(); 1184 1185 // Test example MIME type (doc). 1186 $this->assertEquals('application/msword', $mimeinfo['doc']['type']); 1187 $this->assertEquals('document', $mimeinfo['doc']['icon']); 1188 $this->assertEquals(array('document'), $mimeinfo['doc']['groups']); 1189 $this->assertFalse(isset($mimeinfo['doc']['string'])); 1190 $this->assertFalse(isset($mimeinfo['doc']['defaulticon'])); 1191 $this->assertFalse(isset($mimeinfo['doc']['customdescription'])); 1192 1193 // Check the less common fields using other examples. 1194 $this->assertEquals('image', $mimeinfo['png']['string']); 1195 $this->assertEquals(true, $mimeinfo['txt']['defaulticon']); 1196 } 1197 1198 /** 1199 * Tests for get_mimetype_for_sending function. 1200 */ 1201 public function test_get_mimetype_for_sending() { 1202 // Without argument. 1203 $this->assertEquals('application/octet-stream', get_mimetype_for_sending()); 1204 1205 // Argument is null. 1206 $this->assertEquals('application/octet-stream', get_mimetype_for_sending(null)); 1207 1208 // Filename having no extension. 1209 $this->assertEquals('application/octet-stream', get_mimetype_for_sending('filenamewithoutextension')); 1210 1211 // Test using the extensions listed from the get_mimetypes_array function. 1212 $mimetypes = get_mimetypes_array(); 1213 foreach ($mimetypes as $ext => $info) { 1214 if ($ext === 'xxx') { 1215 $this->assertEquals('application/octet-stream', get_mimetype_for_sending('SampleFile.' . $ext)); 1216 } else { 1217 $this->assertEquals($info['type'], get_mimetype_for_sending('SampleFile.' . $ext)); 1218 } 1219 } 1220 } 1221 1222 /** 1223 * Test curl agent settings. 1224 */ 1225 public function test_curl_useragent() { 1226 $curl = new testable_curl(); 1227 $options = $curl->get_options(); 1228 $this->assertNotEmpty($options); 1229 1230 $moodlebot = \core_useragent::get_moodlebot_useragent(); 1231 1232 $curl->call_apply_opt($options); 1233 $this->assertTrue(in_array("User-Agent: $moodlebot", $curl->header)); 1234 $this->assertFalse(in_array('User-Agent: Test/1.0', $curl->header)); 1235 1236 $options['CURLOPT_USERAGENT'] = 'Test/1.0'; 1237 $curl->call_apply_opt($options); 1238 $this->assertTrue(in_array('User-Agent: Test/1.0', $curl->header)); 1239 $this->assertFalse(in_array("User-Agent: $moodlebot", $curl->header)); 1240 1241 $curl->set_option('CURLOPT_USERAGENT', 'AnotherUserAgent/1.0'); 1242 $curl->call_apply_opt(); 1243 $this->assertTrue(in_array('User-Agent: AnotherUserAgent/1.0', $curl->header)); 1244 $this->assertFalse(in_array('User-Agent: Test/1.0', $curl->header)); 1245 1246 $curl->set_option('CURLOPT_USERAGENT', 'AnotherUserAgent/1.1'); 1247 $options = $curl->get_options(); 1248 $curl->call_apply_opt($options); 1249 $this->assertTrue(in_array('User-Agent: AnotherUserAgent/1.1', $curl->header)); 1250 $this->assertFalse(in_array('User-Agent: AnotherUserAgent/1.0', $curl->header)); 1251 1252 $curl->unset_option('CURLOPT_USERAGENT'); 1253 $curl->call_apply_opt(); 1254 $this->assertTrue(in_array("User-Agent: $moodlebot", $curl->header)); 1255 1256 // Finally, test it via exttests, to ensure the agent is sent properly. 1257 $testurl = $this->getExternalTestFileUrl('/test_agent.php'); 1258 $extcurl = new \curl(); 1259 1260 // Matching (assert we don't receive an error, and get back the content "OK"). 1261 $contents = $extcurl->get($testurl, array(), array('CURLOPT_USERAGENT' => 'AnotherUserAgent/1.2')); 1262 $this->assertSame(0, $extcurl->get_errno()); 1263 $this->assertSame('OK', $contents); 1264 1265 // Not matching (assert we don't receive an error, and get back empty content - not "OK"). 1266 $contents = $extcurl->get($testurl, array(), array('CURLOPT_USERAGENT' => 'NonMatchingUserAgent/1.2')); 1267 $this->assertSame(0, $extcurl->get_errno()); 1268 $this->assertSame('', $contents); 1269 } 1270 1271 /** 1272 * Test file_rewrite_pluginfile_urls. 1273 */ 1274 public function test_file_rewrite_pluginfile_urls() { 1275 1276 $syscontext = \context_system::instance(); 1277 $originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">'; 1278 1279 // Do the rewrite. 1280 $finaltext = file_rewrite_pluginfile_urls($originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0); 1281 $this->assertStringContainsString("pluginfile.php", $finaltext); 1282 1283 // Now undo. 1284 $options = array('reverse' => true); 1285 $finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1286 1287 // Compare the final text is the same that the original. 1288 $this->assertEquals($originaltext, $finaltext); 1289 } 1290 1291 /** 1292 * Test file_rewrite_pluginfile_urls with includetoken. 1293 */ 1294 public function test_file_rewrite_pluginfile_urls_includetoken() { 1295 global $USER, $CFG; 1296 1297 $CFG->slasharguments = true; 1298 1299 $this->resetAfterTest(); 1300 1301 $syscontext = \context_system::instance(); 1302 $originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">'; 1303 $options = ['includetoken' => true]; 1304 1305 // Rewrite the content. This will generate a new token. 1306 $finaltext = file_rewrite_pluginfile_urls( 1307 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1308 1309 $token = get_user_key('core_files', $USER->id); 1310 $expectedurl = new \moodle_url("/tokenpluginfile.php/{$token}/{$syscontext->id}/user/private/0/image.png"); 1311 $expectedtext = "Fake test with an image <img src=\"{$expectedurl}\">"; 1312 $this->assertEquals($expectedtext, $finaltext); 1313 1314 // Do it again - the second time will use an existing token. 1315 $finaltext = file_rewrite_pluginfile_urls( 1316 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1317 $this->assertEquals($expectedtext, $finaltext); 1318 1319 // Now undo. 1320 $options['reverse'] = true; 1321 $finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1322 1323 // Compare the final text is the same that the original. 1324 $this->assertEquals($originaltext, $finaltext); 1325 1326 // Now indicates a user different than $USER. 1327 $user = $this->getDataGenerator()->create_user(); 1328 $options = ['includetoken' => $user->id]; 1329 1330 // Rewrite the content. This will generate a new token. 1331 $finaltext = file_rewrite_pluginfile_urls( 1332 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1333 1334 $token = get_user_key('core_files', $user->id); 1335 $expectedurl = new \moodle_url("/tokenpluginfile.php/{$token}/{$syscontext->id}/user/private/0/image.png"); 1336 $expectedtext = "Fake test with an image <img src=\"{$expectedurl}\">"; 1337 $this->assertEquals($expectedtext, $finaltext); 1338 } 1339 1340 /** 1341 * Test file_rewrite_pluginfile_urls with includetoken with slasharguments disabled.. 1342 */ 1343 public function test_file_rewrite_pluginfile_urls_includetoken_no_slashargs() { 1344 global $USER, $CFG; 1345 1346 $CFG->slasharguments = false; 1347 1348 $this->resetAfterTest(); 1349 1350 $syscontext = \context_system::instance(); 1351 $originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">'; 1352 $options = ['includetoken' => true]; 1353 1354 // Rewrite the content. This will generate a new token. 1355 $finaltext = file_rewrite_pluginfile_urls( 1356 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1357 1358 $token = get_user_key('core_files', $USER->id); 1359 $expectedurl = new \moodle_url("/tokenpluginfile.php"); 1360 $expectedurl .= "?token={$token}&file=/{$syscontext->id}/user/private/0/image.png"; 1361 $expectedtext = "Fake test with an image <img src=\"{$expectedurl}\">"; 1362 $this->assertEquals($expectedtext, $finaltext); 1363 1364 // Do it again - the second time will use an existing token. 1365 $finaltext = file_rewrite_pluginfile_urls( 1366 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1367 $this->assertEquals($expectedtext, $finaltext); 1368 1369 // Now undo. 1370 $options['reverse'] = true; 1371 $finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1372 1373 // Compare the final text is the same that the original. 1374 $this->assertEquals($originaltext, $finaltext); 1375 } 1376 1377 /** 1378 * Helpter function to create draft files 1379 * 1380 * @param array $filedata data for the file record (to not use defaults) 1381 * @return stored_file the stored file instance 1382 */ 1383 public static function create_draft_file($filedata = array()) { 1384 global $USER; 1385 1386 $fs = get_file_storage(); 1387 1388 $filerecord = array( 1389 'component' => 'user', 1390 'filearea' => 'draft', 1391 'itemid' => isset($filedata['itemid']) ? $filedata['itemid'] : file_get_unused_draft_itemid(), 1392 'author' => isset($filedata['author']) ? $filedata['author'] : fullname($USER), 1393 'filepath' => isset($filedata['filepath']) ? $filedata['filepath'] : '/', 1394 'filename' => isset($filedata['filename']) ? $filedata['filename'] : 'file.txt', 1395 ); 1396 1397 if (isset($filedata['contextid'])) { 1398 $filerecord['contextid'] = $filedata['contextid']; 1399 } else { 1400 $usercontext = \context_user::instance($USER->id); 1401 $filerecord['contextid'] = $usercontext->id; 1402 } 1403 $source = isset($filedata['source']) ? $filedata['source'] : serialize((object)array('source' => 'From string')); 1404 $content = isset($filedata['content']) ? $filedata['content'] : 'some content here'; 1405 1406 $file = $fs->create_file_from_string($filerecord, $content); 1407 $file->set_source($source); 1408 1409 return $file; 1410 } 1411 1412 /** 1413 * Test file_merge_files_from_draft_area_into_filearea 1414 */ 1415 public function test_file_merge_files_from_draft_area_into_filearea() { 1416 global $USER, $CFG; 1417 1418 $this->resetAfterTest(true); 1419 $this->setAdminUser(); 1420 $fs = get_file_storage(); 1421 $usercontext = \context_user::instance($USER->id); 1422 1423 // Create a draft file. 1424 $filename = 'data.txt'; 1425 $filerecord = array( 1426 'filename' => $filename, 1427 ); 1428 $file = self::create_draft_file($filerecord); 1429 $draftitemid = $file->get_itemid(); 1430 1431 $maxbytes = $CFG->userquota; 1432 $maxareabytes = $CFG->userquota; 1433 $options = array('subdirs' => 1, 1434 'maxbytes' => $maxbytes, 1435 'maxfiles' => -1, 1436 'areamaxbytes' => $maxareabytes); 1437 1438 // Add new file. 1439 file_merge_files_from_draft_area_into_filearea($draftitemid, $usercontext->id, 'user', 'private', 0, $options); 1440 1441 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1442 // Directory and file. 1443 $this->assertCount(2, $files); 1444 $found = false; 1445 foreach ($files as $file) { 1446 if (!$file->is_directory()) { 1447 $found = true; 1448 $this->assertEquals($filename, $file->get_filename()); 1449 $this->assertEquals('some content here', $file->get_content()); 1450 } 1451 } 1452 $this->assertTrue($found); 1453 1454 // Add two more files. 1455 $filerecord = array( 1456 'itemid' => $draftitemid, 1457 'filename' => 'second.txt', 1458 ); 1459 self::create_draft_file($filerecord); 1460 $filerecord = array( 1461 'itemid' => $draftitemid, 1462 'filename' => 'third.txt', 1463 ); 1464 $file = self::create_draft_file($filerecord); 1465 1466 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options); 1467 1468 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1469 $this->assertCount(4, $files); 1470 1471 // Update contents of one file. 1472 $filerecord = array( 1473 'filename' => 'second.txt', 1474 'content' => 'new content', 1475 ); 1476 $file = self::create_draft_file($filerecord); 1477 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options); 1478 1479 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1480 $this->assertCount(4, $files); 1481 $found = false; 1482 foreach ($files as $file) { 1483 if ($file->get_filename() == 'second.txt') { 1484 $found = true; 1485 $this->assertEquals('new content', $file->get_content()); 1486 } 1487 } 1488 $this->assertTrue($found); 1489 1490 // Update author. 1491 // Set different author in the current file. 1492 foreach ($files as $file) { 1493 if ($file->get_filename() == 'second.txt') { 1494 $file->set_author('Nobody'); 1495 } 1496 } 1497 $filerecord = array( 1498 'filename' => 'second.txt', 1499 ); 1500 $file = self::create_draft_file($filerecord); 1501 1502 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options); 1503 1504 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1505 $this->assertCount(4, $files); 1506 $found = false; 1507 foreach ($files as $file) { 1508 if ($file->get_filename() == 'second.txt') { 1509 $found = true; 1510 $this->assertEquals(fullname($USER), $file->get_author()); 1511 } 1512 } 1513 $this->assertTrue($found); 1514 1515 } 1516 1517 /** 1518 * Test max area bytes for file_merge_files_from_draft_area_into_filearea 1519 */ 1520 public function test_file_merge_files_from_draft_area_into_filearea_max_area_bytes() { 1521 global $USER; 1522 1523 $this->resetAfterTest(true); 1524 $this->setAdminUser(); 1525 $fs = get_file_storage(); 1526 1527 $file = self::create_draft_file(); 1528 $options = array('subdirs' => 1, 1529 'maxbytes' => 5, 1530 'maxfiles' => -1, 1531 'areamaxbytes' => 10); 1532 1533 // Add new file. 1534 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options); 1535 $usercontext = \context_user::instance($USER->id); 1536 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1537 $this->assertCount(0, $files); 1538 } 1539 1540 /** 1541 * Test max file bytes for file_merge_files_from_draft_area_into_filearea 1542 */ 1543 public function test_file_merge_files_from_draft_area_into_filearea_max_file_bytes() { 1544 global $USER; 1545 1546 $this->resetAfterTest(true); 1547 // The admin has no restriction for max file uploads, so use a normal user. 1548 $user = $this->getDataGenerator()->create_user(); 1549 $this->setUser($user); 1550 $fs = get_file_storage(); 1551 1552 $file = self::create_draft_file(); 1553 $options = array('subdirs' => 1, 1554 'maxbytes' => 1, 1555 'maxfiles' => -1, 1556 'areamaxbytes' => 100); 1557 1558 // Add new file. 1559 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options); 1560 $usercontext = \context_user::instance($USER->id); 1561 // Check we only get the base directory, not a new file. 1562 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1563 $this->assertCount(1, $files); 1564 $file = array_shift($files); 1565 $this->assertTrue($file->is_directory()); 1566 } 1567 1568 /** 1569 * Test max file number for file_merge_files_from_draft_area_into_filearea 1570 */ 1571 public function test_file_merge_files_from_draft_area_into_filearea_max_files() { 1572 global $USER; 1573 1574 $this->resetAfterTest(true); 1575 $this->setAdminUser(); 1576 $fs = get_file_storage(); 1577 1578 $file = self::create_draft_file(); 1579 $options = array('subdirs' => 1, 1580 'maxbytes' => 1000, 1581 'maxfiles' => 0, 1582 'areamaxbytes' => 1000); 1583 1584 // Add new file. 1585 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options); 1586 $usercontext = \context_user::instance($USER->id); 1587 // Check we only get the base directory, not a new file. 1588 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1589 $this->assertCount(1, $files); 1590 $file = array_shift($files); 1591 $this->assertTrue($file->is_directory()); 1592 } 1593 1594 /** 1595 * Test file_get_draft_area_info. 1596 */ 1597 public function test_file_get_draft_area_info() { 1598 global $USER; 1599 1600 $this->resetAfterTest(true); 1601 $this->setAdminUser(); 1602 $fs = get_file_storage(); 1603 1604 $filerecord = array( 1605 'filename' => 'one.txt', 1606 ); 1607 $file = self::create_draft_file($filerecord); 1608 $size = $file->get_filesize(); 1609 $draftitemid = $file->get_itemid(); 1610 // Add another file. 1611 $filerecord = array( 1612 'itemid' => $draftitemid, 1613 'filename' => 'second.txt', 1614 ); 1615 $file = self::create_draft_file($filerecord); 1616 $size += $file->get_filesize(); 1617 1618 // Create directory. 1619 $usercontext = \context_user::instance($USER->id); 1620 $dir = $fs->create_directory($usercontext->id, 'user', 'draft', $draftitemid, '/testsubdir/'); 1621 // Add file to directory. 1622 $filerecord = array( 1623 'itemid' => $draftitemid, 1624 'filename' => 'third.txt', 1625 'filepath' => '/testsubdir/', 1626 ); 1627 $file = self::create_draft_file($filerecord); 1628 $size += $file->get_filesize(); 1629 1630 $fileinfo = file_get_draft_area_info($draftitemid); 1631 $this->assertEquals(3, $fileinfo['filecount']); 1632 $this->assertEquals($size, $fileinfo['filesize']); 1633 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created. 1634 $this->assertEquals($size, $fileinfo['filesize_without_references']); 1635 1636 // Now get files from just one folder. 1637 $fileinfo = file_get_draft_area_info($draftitemid, '/testsubdir/'); 1638 $this->assertEquals(1, $fileinfo['filecount']); 1639 $this->assertEquals($file->get_filesize(), $fileinfo['filesize']); 1640 $this->assertEquals(0, $fileinfo['foldercount']); // No subdirectories inside the directory. 1641 $this->assertEquals($file->get_filesize(), $fileinfo['filesize_without_references']); 1642 1643 // Check we get the same results if we call file_get_file_area_info. 1644 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'draft', $draftitemid); 1645 $this->assertEquals(3, $fileinfo['filecount']); 1646 $this->assertEquals($size, $fileinfo['filesize']); 1647 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created. 1648 $this->assertEquals($size, $fileinfo['filesize_without_references']); 1649 } 1650 1651 /** 1652 * Test file_get_file_area_info. 1653 */ 1654 public function test_file_get_file_area_info() { 1655 global $USER; 1656 1657 $this->resetAfterTest(true); 1658 $this->setAdminUser(); 1659 $fs = get_file_storage(); 1660 1661 $filerecord = array( 1662 'filename' => 'one.txt', 1663 ); 1664 $file = self::create_draft_file($filerecord); 1665 $size = $file->get_filesize(); 1666 $draftitemid = $file->get_itemid(); 1667 // Add another file. 1668 $filerecord = array( 1669 'itemid' => $draftitemid, 1670 'filename' => 'second.txt', 1671 ); 1672 $file = self::create_draft_file($filerecord); 1673 $size += $file->get_filesize(); 1674 1675 // Create directory. 1676 $usercontext = \context_user::instance($USER->id); 1677 $dir = $fs->create_directory($usercontext->id, 'user', 'draft', $draftitemid, '/testsubdir/'); 1678 // Add file to directory. 1679 $filerecord = array( 1680 'itemid' => $draftitemid, 1681 'filename' => 'third.txt', 1682 'filepath' => '/testsubdir/', 1683 ); 1684 $file = self::create_draft_file($filerecord); 1685 $size += $file->get_filesize(); 1686 1687 // Add files to user private file area. 1688 $options = array('subdirs' => 1, 'maxfiles' => 3); 1689 file_merge_files_from_draft_area_into_filearea($draftitemid, $file->get_contextid(), 'user', 'private', 0, $options); 1690 1691 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'private'); 1692 $this->assertEquals(3, $fileinfo['filecount']); 1693 $this->assertEquals($size, $fileinfo['filesize']); 1694 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created. 1695 $this->assertEquals($size, $fileinfo['filesize_without_references']); 1696 1697 // Now get files from just one folder. 1698 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'private', 0, '/testsubdir/'); 1699 $this->assertEquals(1, $fileinfo['filecount']); 1700 $this->assertEquals($file->get_filesize(), $fileinfo['filesize']); 1701 $this->assertEquals(0, $fileinfo['foldercount']); // No subdirectories inside the directory. 1702 $this->assertEquals($file->get_filesize(), $fileinfo['filesize_without_references']); 1703 } 1704 1705 /** 1706 * Test confirming that draft files not referenced in the editor text are removed. 1707 */ 1708 public function test_file_remove_editor_orphaned_files() { 1709 global $USER, $CFG; 1710 $this->resetAfterTest(true); 1711 $this->setAdminUser(); 1712 1713 // Create three draft files. 1714 $filerecord = ['filename' => 'file1.png']; 1715 $file = self::create_draft_file($filerecord); 1716 $draftitemid = $file->get_itemid(); 1717 1718 $filerecord['itemid'] = $draftitemid; 1719 1720 $filerecord['filename'] = 'file2.png'; 1721 self::create_draft_file($filerecord); 1722 1723 $filerecord['filename'] = 'file 3.png'; 1724 self::create_draft_file($filerecord); 1725 1726 $filerecord['filename'] = 'file4.png'; 1727 self::create_draft_file($filerecord); 1728 1729 // Confirm the user drafts area lists 3 files. 1730 $fs = get_file_storage(); 1731 $usercontext = \context_user::instance($USER->id); 1732 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'itemid', 0); 1733 $this->assertCount(4, $draftfiles); 1734 1735 // Now, spoof some editor text content, referencing 2 of the files; one requiring name encoding, one not. 1736 $editor = [ 1737 'itemid' => $draftitemid, 1738 'text' => " 1739 <img src=\"{$CFG->wwwroot}/draftfile.php/{$usercontext->id}/user/draft/{$draftitemid}/file%203.png\" alt=\"\"> 1740 <img src=\"{$CFG->wwwroot}/draftfile.php/{$usercontext->id}/user/draft/{$draftitemid}/file1.png\" alt=\"\"> 1741 <span>{$CFG->wwwroot}/draftfile.php/{$usercontext->id}/user/draft/{$draftitemid}/file4.png</span>" 1742 ]; 1743 1744 // Run the remove orphaned drafts function and confirm that only the referenced files remain in the user drafts. 1745 // The drafts we expect will not be removed (are referenced in the online text). 1746 $expected = ['file1.png', 'file 3.png', 'file4.png']; 1747 file_remove_editor_orphaned_files($editor); 1748 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'itemid', 0); 1749 $this->assertCount(3, $draftfiles); 1750 foreach ($draftfiles as $file) { 1751 $this->assertContains($file->get_filename(), $expected); 1752 } 1753 } 1754 1755 /** 1756 * Test that all files in the draftarea are returned. 1757 */ 1758 public function test_file_get_all_files_in_draftarea() { 1759 $this->resetAfterTest(); 1760 $this->setAdminUser(); 1761 1762 $filerecord = ['filename' => 'basepic.jpg']; 1763 $file = self::create_draft_file($filerecord); 1764 1765 $secondrecord = [ 1766 'filename' => 'infolder.jpg', 1767 'filepath' => '/assignment/', 1768 'itemid' => $file->get_itemid() 1769 ]; 1770 $file = self::create_draft_file($secondrecord); 1771 1772 $thirdrecord = [ 1773 'filename' => 'deeperfolder.jpg', 1774 'filepath' => '/assignment/pics/', 1775 'itemid' => $file->get_itemid() 1776 ]; 1777 $file = self::create_draft_file($thirdrecord); 1778 1779 $fourthrecord = [ 1780 'filename' => 'differentimage.jpg', 1781 'filepath' => '/secondfolder/', 1782 'itemid' => $file->get_itemid() 1783 ]; 1784 $file = self::create_draft_file($fourthrecord); 1785 1786 // This record has the same name as the last record, but it's in a different folder. 1787 // Just checking this is also returned. 1788 $fifthrecord = [ 1789 'filename' => 'differentimage.jpg', 1790 'filepath' => '/assignment/pics/', 1791 'itemid' => $file->get_itemid() 1792 ]; 1793 $file = self::create_draft_file($fifthrecord); 1794 1795 $allfiles = file_get_all_files_in_draftarea($file->get_itemid()); 1796 $this->assertCount(5, $allfiles); 1797 $this->assertEquals($filerecord['filename'], $allfiles[0]->filename); 1798 $this->assertEquals($secondrecord['filename'], $allfiles[1]->filename); 1799 $this->assertEquals($thirdrecord['filename'], $allfiles[2]->filename); 1800 $this->assertEquals($fourthrecord['filename'], $allfiles[3]->filename); 1801 $this->assertEquals($fifthrecord['filename'], $allfiles[4]->filename); 1802 } 1803 1804 public function test_file_copy_file_to_file_area() { 1805 // Create two files in different draft areas but owned by the same user. 1806 global $USER; 1807 $this->resetAfterTest(true); 1808 $this->setAdminUser(); 1809 1810 $filerecord = ['filename' => 'file1.png', 'itemid' => file_get_unused_draft_itemid()]; 1811 $file1 = self::create_draft_file($filerecord); 1812 $filerecord = ['filename' => 'file2.png', 'itemid' => file_get_unused_draft_itemid()]; 1813 $file2 = self::create_draft_file($filerecord); 1814 1815 // Confirm one file in each draft area. 1816 $fs = get_file_storage(); 1817 $usercontext = \context_user::instance($USER->id); 1818 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file1->get_itemid(), 'itemid', 0); 1819 $this->assertCount(1, $draftfiles); 1820 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file2->get_itemid(), 'itemid', 0); 1821 $this->assertCount(1, $draftfiles); 1822 1823 // Create file record. 1824 $filerecord = [ 1825 'component' => $file2->get_component(), 1826 'filearea' => $file2->get_filearea(), 1827 'itemid' => $file2->get_itemid(), 1828 'contextid' => $file2->get_contextid(), 1829 'filepath' => '/', 1830 'filename' => $file2->get_filename() 1831 ]; 1832 1833 // Copy file2 into file1's draft area. 1834 file_copy_file_to_file_area($filerecord, $file2->get_filename(), $file1->get_itemid()); 1835 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file1->get_itemid(), 'itemid', 0); 1836 $this->assertCount(2, $draftfiles); 1837 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file2->get_itemid(), 'itemid', 0); 1838 $this->assertCount(1, $draftfiles); 1839 } 1840 1841 /** 1842 * Test file_is_draft_areas_limit_reached 1843 */ 1844 public function test_file_is_draft_areas_limit_reached() { 1845 global $CFG; 1846 $this->resetAfterTest(true); 1847 1848 $capacity = $CFG->draft_area_bucket_capacity = 5; 1849 $leak = $CFG->draft_area_bucket_leak = 0.2; // Leaks every 5 seconds. 1850 1851 $generator = $this->getDataGenerator(); 1852 $user = $generator->create_user(); 1853 1854 $this->setUser($user); 1855 1856 $itemids = []; 1857 for ($i = 0; $i < $capacity; $i++) { 1858 $itemids[$i] = file_get_unused_draft_itemid(); 1859 } 1860 1861 // This test highly depends on time. We try to make sure that the test starts at the early moments on the second. 1862 // This was not needed if MDL-37327 was implemented. 1863 $after = time(); 1864 while (time() === $after) { 1865 usleep(100000); 1866 } 1867 1868 // Burst up to the capacity and make sure that the bucket allows it. 1869 $burststart = microtime(); 1870 for ($i = 0; $i < $capacity; $i++) { 1871 if ($i) { 1872 sleep(1); // A little delay so we have different timemodified value for files. 1873 } 1874 $this->assertFalse(file_is_draft_areas_limit_reached($user->id)); 1875 self::create_draft_file([ 1876 'filename' => 'file1.png', 1877 'itemid' => $itemids[$i], 1878 ]); 1879 } 1880 1881 // The bucket should be full after bursting. 1882 $this->assertTrue(file_is_draft_areas_limit_reached($user->id)); 1883 1884 // Calculate the time taken to burst up the bucket capacity. 1885 $timetaken = microtime_diff($burststart, microtime()); 1886 1887 // The bucket leaks so it shouldn't be full after a certain time. 1888 // Items are added into the bucket at the rate of 1 item per second. 1889 // One item leaks from the bucket every 1/$leak seconds. 1890 // So it takes 1/$leak - ($capacity-1) seconds for the bucket to leak one item and not be full anymore. 1891 $milliseconds = ceil(1000000 * ((1 / $leak) - ($capacity - 1)) - ($timetaken * 1000)); 1892 usleep($milliseconds); 1893 1894 $this->assertFalse(file_is_draft_areas_limit_reached($user->id)); 1895 1896 // Only one item was leaked from the bucket. So the bucket should become full again if we add a single item to it. 1897 self::create_draft_file([ 1898 'filename' => 'file2.png', 1899 'itemid' => $itemids[0], 1900 ]); 1901 $this->assertTrue(file_is_draft_areas_limit_reached($user->id)); 1902 1903 // The bucket leaks at a constant rate. It doesn't matter if it is filled as the result of bursting or not. 1904 sleep(ceil(1 / $leak)); 1905 $this->assertFalse(file_is_draft_areas_limit_reached($user->id)); 1906 } 1907 1908 /** 1909 * Test text cleaning when preparing text editor data. 1910 * 1911 * @covers ::file_prepare_standard_editor 1912 */ 1913 public function test_file_prepare_standard_editor_clean_text() { 1914 $text = "lala <object>xx</object>"; 1915 1916 $syscontext = \context_system::instance(); 1917 1918 $object = new \stdClass(); 1919 $object->some = $text; 1920 $object->someformat = FORMAT_PLAIN; 1921 1922 $result = file_prepare_standard_editor(clone($object), 'some', 1923 ['noclean' => false]); 1924 $this->assertSame($text, $result->some); 1925 $result = file_prepare_standard_editor(clone($object), 'some', 1926 ['noclean' => true]); 1927 $this->assertSame($text, $result->some); 1928 $result = file_prepare_standard_editor(clone($object), 'some', 1929 ['noclean' => false, 'context' => $syscontext], $syscontext, 'core', 'some', 1); 1930 $this->assertSame($text, $result->some); 1931 $result = file_prepare_standard_editor(clone($object), 'some', 1932 ['noclean' => true, 'context' => $syscontext], $syscontext, 'core', 'some', 1); 1933 $this->assertSame($text, $result->some); 1934 1935 $object = new \stdClass(); 1936 $object->some = $text; 1937 $object->someformat = FORMAT_MARKDOWN; 1938 1939 $result = file_prepare_standard_editor(clone($object), 'some', 1940 ['noclean' => false]); 1941 $this->assertSame($text, $result->some); 1942 $result = file_prepare_standard_editor(clone($object), 'some', 1943 ['noclean' => true]); 1944 $this->assertSame($text, $result->some); 1945 $result = file_prepare_standard_editor(clone($object), 'some', 1946 ['noclean' => false, 'context' => $syscontext], $syscontext, 'core', 'some', 1); 1947 $this->assertSame($text, $result->some); 1948 $result = file_prepare_standard_editor(clone($object), 'some', 1949 ['noclean' => true, 'context' => $syscontext], $syscontext, 'core', 'some', 1); 1950 $this->assertSame($text, $result->some); 1951 1952 $object = new \stdClass(); 1953 $object->some = $text; 1954 $object->someformat = FORMAT_MOODLE; 1955 1956 $result = file_prepare_standard_editor(clone($object), 'some', 1957 ['noclean' => false]); 1958 $this->assertSame('lala xx', $result->some); 1959 $result = file_prepare_standard_editor(clone($object), 'some', 1960 ['noclean' => true]); 1961 $this->assertSame($text, $result->some); 1962 $result = file_prepare_standard_editor(clone($object), 'some', 1963 ['noclean' => false, 'context' => $syscontext], $syscontext, 'core', 'some', 1); 1964 $this->assertSame('lala xx', $result->some); 1965 $result = file_prepare_standard_editor(clone($object), 'some', 1966 ['noclean' => true, 'context' => $syscontext], $syscontext, 'core', 'some', 1); 1967 $this->assertSame($text, $result->some); 1968 1969 $object = new \stdClass(); 1970 $object->some = $text; 1971 $object->someformat = FORMAT_HTML; 1972 1973 $result = file_prepare_standard_editor(clone($object), 'some', 1974 ['noclean' => false]); 1975 $this->assertSame('lala xx', $result->some); 1976 $result = file_prepare_standard_editor(clone($object), 'some', 1977 ['noclean' => true]); 1978 $this->assertSame($text, $result->some); 1979 $result = file_prepare_standard_editor(clone($object), 'some', 1980 ['noclean' => false, 'context' => $syscontext], $syscontext, 'core', 'some', 1); 1981 $this->assertSame('lala xx', $result->some); 1982 $result = file_prepare_standard_editor(clone($object), 'some', 1983 ['noclean' => true, 'context' => $syscontext], $syscontext, 'core', 'some', 1); 1984 $this->assertSame($text, $result->some); 1985 } 1986 1987 /** 1988 * Tests for file_get_typegroup to check that both arrays, and string values are accepted. 1989 * 1990 * @dataProvider file_get_typegroup_provider 1991 * @param string|array $group 1992 * @param string $expected 1993 */ 1994 public function test_file_get_typegroup( 1995 $group, 1996 string $expected 1997 ): void { 1998 $result = file_get_typegroup('type', $group); 1999 $this->assertContains($expected, $result); 2000 } 2001 2002 public static function file_get_typegroup_provider(): array { 2003 return [ 2004 'Array of values' => [ 2005 ['.html', '.htm'], 2006 'text/html', 2007 ], 2008 'String of comma-separated values' => [ 2009 '.html, .htm', 2010 'text/html', 2011 ], 2012 'String of colon-separated values' => [ 2013 '.html : .htm', 2014 'text/html', 2015 ], 2016 'String of semi-colon-separated values' => [ 2017 '.html ; .htm', 2018 'text/html', 2019 ], 2020 ]; 2021 } 2022 } 2023 2024 /** 2025 * Test-specific class to allow easier testing of curl functions. 2026 * 2027 * @copyright 2015 Dave Cooper 2028 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 2029 */ 2030 class testable_curl extends curl { 2031 /** 2032 * Accessor for private options array using reflection. 2033 * 2034 * @return array 2035 */ 2036 public function get_options() { 2037 // Access to private property. 2038 $rp = new \ReflectionProperty('curl', 'options'); 2039 $rp->setAccessible(true); 2040 return $rp->getValue($this); 2041 } 2042 2043 /** 2044 * Setter for private options array using reflection. 2045 * 2046 * @param array $options 2047 */ 2048 public function set_options($options) { 2049 // Access to private property. 2050 $rp = new \ReflectionProperty('curl', 'options'); 2051 $rp->setAccessible(true); 2052 $rp->setValue($this, $options); 2053 } 2054 2055 /** 2056 * Setter for individual option. 2057 * @param string $option 2058 * @param string $value 2059 */ 2060 public function set_option($option, $value) { 2061 $options = $this->get_options(); 2062 $options[$option] = $value; 2063 $this->set_options($options); 2064 } 2065 2066 /** 2067 * Unsets an option on the curl object 2068 * @param string $option 2069 */ 2070 public function unset_option($option) { 2071 $options = $this->get_options(); 2072 unset($options[$option]); 2073 $this->set_options($options); 2074 } 2075 2076 /** 2077 * Wrapper to access the private \curl::apply_opt() method using reflection. 2078 * 2079 * @param array $options 2080 * @return resource The curl handle 2081 */ 2082 public function call_apply_opt($options = null) { 2083 // Access to private method. 2084 $rm = new \ReflectionMethod('curl', 'apply_opt'); 2085 $rm->setAccessible(true); 2086 $ch = curl_init(); 2087 return $rm->invoke($this, $ch, $options); 2088 } 2089 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body