Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
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 * Tests the strip_double_headers function in the curl class. 878 */ 879 public function test_curl_strip_double_headers() { 880 // Example from issue tracker. 881 $mdl30648example = <<<EOF 882 HTTP/1.0 407 Proxy Authentication Required 883 Server: squid/2.7.STABLE9 884 Date: Thu, 08 Dec 2011 14:44:33 GMT 885 Content-Type: text/html 886 Content-Length: 1275 887 X-Squid-Error: ERR_CACHE_ACCESS_DENIED 0 888 Proxy-Authenticate: Basic realm="Squid proxy-caching web server" 889 X-Cache: MISS from homer.lancs.ac.uk 890 X-Cache-Lookup: NONE from homer.lancs.ac.uk:3128 891 Via: 1.0 homer.lancs.ac.uk:3128 (squid/2.7.STABLE9) 892 Connection: close 893 894 HTTP/1.0 200 OK 895 Server: Apache 896 X-Lb-Nocache: true 897 Cache-Control: private, max-age=15, no-transform 898 ETag: "4d69af5d8ba873ea9192c489e151bd7b" 899 Content-Type: text/html 900 Date: Thu, 08 Dec 2011 14:44:53 GMT 901 Set-Cookie: BBC-UID=c4de2e109c8df6a51de627cee11b214bd4fb6054a030222488317afb31b343360MoodleBot/1.0; expires=Mon, 07-Dec-15 14:44:53 GMT; path=/; domain=bbc.co.uk 902 X-Cache-Action: MISS 903 X-Cache-Age: 0 904 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 905 X-Cache: MISS from ww 906 907 <html>... 908 EOF; 909 $mdl30648expected = <<<EOF 910 HTTP/1.0 200 OK 911 Server: Apache 912 X-Lb-Nocache: true 913 Cache-Control: private, max-age=15, no-transform 914 ETag: "4d69af5d8ba873ea9192c489e151bd7b" 915 Content-Type: text/html 916 Date: Thu, 08 Dec 2011 14:44:53 GMT 917 Set-Cookie: BBC-UID=c4de2e109c8df6a51de627cee11b214bd4fb6054a030222488317afb31b343360MoodleBot/1.0; expires=Mon, 07-Dec-15 14:44:53 GMT; path=/; domain=bbc.co.uk 918 X-Cache-Action: MISS 919 X-Cache-Age: 0 920 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 921 X-Cache: MISS from ww 922 923 <html>... 924 EOF; 925 // For HTTP, replace the \n with \r\n. 926 $mdl30648example = preg_replace("~(?!<\r)\n~", "\r\n", $mdl30648example); 927 $mdl30648expected = preg_replace("~(?!<\r)\n~", "\r\n", $mdl30648expected); 928 929 // Test stripping works OK. 930 $this->assertSame($mdl30648expected, \curl::strip_double_headers($mdl30648example)); 931 // Test it does nothing to the 'plain' data. 932 $this->assertSame($mdl30648expected, \curl::strip_double_headers($mdl30648expected)); 933 934 // Example from OU proxy. 935 $httpsexample = <<<EOF 936 HTTP/1.0 200 Connection established 937 938 HTTP/1.1 200 OK 939 Date: Fri, 22 Feb 2013 17:14:23 GMT 940 Server: Apache/2 941 X-Powered-By: PHP/5.3.3-7+squeeze14 942 Content-Type: text/xml 943 Connection: close 944 Content-Encoding: gzip 945 Transfer-Encoding: chunked 946 947 <?xml version="1.0" encoding="ISO-8859-1" ?> 948 <rss version="2.0">... 949 EOF; 950 $httpsexpected = <<<EOF 951 HTTP/1.1 200 OK 952 Date: Fri, 22 Feb 2013 17:14:23 GMT 953 Server: Apache/2 954 X-Powered-By: PHP/5.3.3-7+squeeze14 955 Content-Type: text/xml 956 Connection: close 957 Content-Encoding: gzip 958 Transfer-Encoding: chunked 959 960 <?xml version="1.0" encoding="ISO-8859-1" ?> 961 <rss version="2.0">... 962 EOF; 963 // For HTTP, replace the \n with \r\n. 964 $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); 965 $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); 966 967 // Test stripping works OK. 968 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexample)); 969 // Test it does nothing to the 'plain' data. 970 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexpected)); 971 972 $httpsexample = <<<EOF 973 HTTP/1.0 200 Connection established 974 975 HTTP/2 200 OK 976 Date: Fri, 22 Feb 2013 17:14:23 GMT 977 Server: Apache/2 978 X-Powered-By: PHP/5.3.3-7+squeeze14 979 Content-Type: text/xml 980 Connection: close 981 Content-Encoding: gzip 982 Transfer-Encoding: chunked 983 984 <?xml version="1.0" encoding="ISO-8859-1" ?> 985 <rss version="2.0">... 986 EOF; 987 $httpsexpected = <<<EOF 988 HTTP/2 200 OK 989 Date: Fri, 22 Feb 2013 17:14:23 GMT 990 Server: Apache/2 991 X-Powered-By: PHP/5.3.3-7+squeeze14 992 Content-Type: text/xml 993 Connection: close 994 Content-Encoding: gzip 995 Transfer-Encoding: chunked 996 997 <?xml version="1.0" encoding="ISO-8859-1" ?> 998 <rss version="2.0">... 999 EOF; 1000 // For HTTP, replace the \n with \r\n. 1001 $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); 1002 $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); 1003 1004 // Test stripping works OK. 1005 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexample)); 1006 // Test it does nothing to the 'plain' data. 1007 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexpected)); 1008 1009 $httpsexample = <<<EOF 1010 HTTP/1.0 200 Connection established 1011 1012 HTTP/2.1 200 OK 1013 Date: Fri, 22 Feb 2013 17:14:23 GMT 1014 Server: Apache/2 1015 X-Powered-By: PHP/5.3.3-7+squeeze14 1016 Content-Type: text/xml 1017 Connection: close 1018 Content-Encoding: gzip 1019 Transfer-Encoding: chunked 1020 1021 <?xml version="1.0" encoding="ISO-8859-1" ?> 1022 <rss version="2.0">... 1023 EOF; 1024 $httpsexpected = <<<EOF 1025 HTTP/2.1 200 OK 1026 Date: Fri, 22 Feb 2013 17:14:23 GMT 1027 Server: Apache/2 1028 X-Powered-By: PHP/5.3.3-7+squeeze14 1029 Content-Type: text/xml 1030 Connection: close 1031 Content-Encoding: gzip 1032 Transfer-Encoding: chunked 1033 1034 <?xml version="1.0" encoding="ISO-8859-1" ?> 1035 <rss version="2.0">... 1036 EOF; 1037 // For HTTP, replace the \n with \r\n. 1038 $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); 1039 $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); 1040 1041 // Test stripping works OK. 1042 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexample)); 1043 // Test it does nothing to the 'plain' data. 1044 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexpected)); 1045 1046 $httpsexample = <<<EOF 1047 HTTP/1.1 200 Connection established 1048 1049 HTTP/3 200 OK 1050 Date: Fri, 22 Feb 2013 17:14:23 GMT 1051 Server: Apache/2 1052 X-Powered-By: PHP/5.3.3-7+squeeze14 1053 Content-Type: text/xml 1054 Connection: close 1055 Content-Encoding: gzip 1056 Transfer-Encoding: chunked 1057 1058 <?xml version="1.0" encoding="ISO-8859-1" ?> 1059 <rss version="2.0">... 1060 EOF; 1061 $httpsexpected = <<<EOF 1062 HTTP/3 200 OK 1063 Date: Fri, 22 Feb 2013 17:14:23 GMT 1064 Server: Apache/2 1065 X-Powered-By: PHP/5.3.3-7+squeeze14 1066 Content-Type: text/xml 1067 Connection: close 1068 Content-Encoding: gzip 1069 Transfer-Encoding: chunked 1070 1071 <?xml version="1.0" encoding="ISO-8859-1" ?> 1072 <rss version="2.0">... 1073 EOF; 1074 // For HTTP, replace the \n with \r\n. 1075 $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); 1076 $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); 1077 1078 // Test stripping works OK. 1079 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexample)); 1080 // Test it does nothing to the 'plain' data. 1081 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexpected)); 1082 1083 $httpsexample = <<<EOF 1084 HTTP/2 200 Connection established 1085 1086 HTTP/4 200 OK 1087 Date: Fri, 22 Feb 2013 17:14:23 GMT 1088 Server: Apache/2 1089 X-Powered-By: PHP/5.3.3-7+squeeze14 1090 Content-Type: text/xml 1091 Connection: close 1092 Content-Encoding: gzip 1093 Transfer-Encoding: chunked 1094 1095 <?xml version="1.0" encoding="ISO-8859-1" ?> 1096 <rss version="2.0">... 1097 EOF; 1098 $httpsexpected = <<<EOF 1099 HTTP/4 200 OK 1100 Date: Fri, 22 Feb 2013 17:14:23 GMT 1101 Server: Apache/2 1102 X-Powered-By: PHP/5.3.3-7+squeeze14 1103 Content-Type: text/xml 1104 Connection: close 1105 Content-Encoding: gzip 1106 Transfer-Encoding: chunked 1107 1108 <?xml version="1.0" encoding="ISO-8859-1" ?> 1109 <rss version="2.0">... 1110 EOF; 1111 // For HTTP, replace the \n with \r\n. 1112 $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample); 1113 $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected); 1114 1115 // Test stripping works OK. 1116 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexample)); 1117 // Test it does nothing to the 'plain' data. 1118 $this->assertSame($httpsexpected, \curl::strip_double_headers($httpsexpected)); 1119 } 1120 1121 /** 1122 * Tests the get_mimetype_description function. 1123 */ 1124 public function test_get_mimetype_description() { 1125 $this->resetAfterTest(); 1126 1127 // Test example type (.doc). 1128 $this->assertEquals(get_string('application/msword', 'mimetypes'), 1129 get_mimetype_description(array('filename' => 'test.doc'))); 1130 1131 // Test an unknown file type. 1132 $this->assertEquals(get_string('document/unknown', 'mimetypes'), 1133 get_mimetype_description(array('filename' => 'test.frog'))); 1134 1135 // Test a custom filetype with no lang string specified. 1136 core_filetypes::add_type('frog', 'application/x-frog', 'document'); 1137 $this->assertEquals('application/x-frog', 1138 get_mimetype_description(array('filename' => 'test.frog'))); 1139 1140 // Test custom description. 1141 core_filetypes::update_type('frog', 'frog', 'application/x-frog', 'document', 1142 array(), '', 'Froggy file'); 1143 $this->assertEquals('Froggy file', 1144 get_mimetype_description(array('filename' => 'test.frog'))); 1145 1146 // Test custom description using multilang filter. 1147 \filter_manager::reset_caches(); 1148 filter_set_global_state('multilang', TEXTFILTER_ON); 1149 filter_set_applies_to_strings('multilang', true); 1150 core_filetypes::update_type('frog', 'frog', 'application/x-frog', 'document', 1151 array(), '', '<span lang="en" class="multilang">Green amphibian</span>' . 1152 '<span lang="fr" class="multilang">Amphibian vert</span>'); 1153 $this->assertEquals('Green amphibian', 1154 get_mimetype_description(array('filename' => 'test.frog'))); 1155 } 1156 1157 /** 1158 * Tests the get_mimetypes_array function. 1159 */ 1160 public function test_get_mimetypes_array() { 1161 $mimeinfo = get_mimetypes_array(); 1162 1163 // Test example MIME type (doc). 1164 $this->assertEquals('application/msword', $mimeinfo['doc']['type']); 1165 $this->assertEquals('document', $mimeinfo['doc']['icon']); 1166 $this->assertEquals(array('document'), $mimeinfo['doc']['groups']); 1167 $this->assertFalse(isset($mimeinfo['doc']['string'])); 1168 $this->assertFalse(isset($mimeinfo['doc']['defaulticon'])); 1169 $this->assertFalse(isset($mimeinfo['doc']['customdescription'])); 1170 1171 // Check the less common fields using other examples. 1172 $this->assertEquals('image', $mimeinfo['png']['string']); 1173 $this->assertEquals(true, $mimeinfo['txt']['defaulticon']); 1174 } 1175 1176 /** 1177 * Tests for get_mimetype_for_sending function. 1178 */ 1179 public function test_get_mimetype_for_sending() { 1180 // Without argument. 1181 $this->assertEquals('application/octet-stream', get_mimetype_for_sending()); 1182 1183 // Argument is null. 1184 $this->assertEquals('application/octet-stream', get_mimetype_for_sending(null)); 1185 1186 // Filename having no extension. 1187 $this->assertEquals('application/octet-stream', get_mimetype_for_sending('filenamewithoutextension')); 1188 1189 // Test using the extensions listed from the get_mimetypes_array function. 1190 $mimetypes = get_mimetypes_array(); 1191 foreach ($mimetypes as $ext => $info) { 1192 if ($ext === 'xxx') { 1193 $this->assertEquals('application/octet-stream', get_mimetype_for_sending('SampleFile.' . $ext)); 1194 } else { 1195 $this->assertEquals($info['type'], get_mimetype_for_sending('SampleFile.' . $ext)); 1196 } 1197 } 1198 } 1199 1200 /** 1201 * Test curl agent settings. 1202 */ 1203 public function test_curl_useragent() { 1204 $curl = new testable_curl(); 1205 $options = $curl->get_options(); 1206 $this->assertNotEmpty($options); 1207 1208 $moodlebot = \core_useragent::get_moodlebot_useragent(); 1209 1210 $curl->call_apply_opt($options); 1211 $this->assertTrue(in_array("User-Agent: $moodlebot", $curl->header)); 1212 $this->assertFalse(in_array('User-Agent: Test/1.0', $curl->header)); 1213 1214 $options['CURLOPT_USERAGENT'] = 'Test/1.0'; 1215 $curl->call_apply_opt($options); 1216 $this->assertTrue(in_array('User-Agent: Test/1.0', $curl->header)); 1217 $this->assertFalse(in_array("User-Agent: $moodlebot", $curl->header)); 1218 1219 $curl->set_option('CURLOPT_USERAGENT', 'AnotherUserAgent/1.0'); 1220 $curl->call_apply_opt(); 1221 $this->assertTrue(in_array('User-Agent: AnotherUserAgent/1.0', $curl->header)); 1222 $this->assertFalse(in_array('User-Agent: Test/1.0', $curl->header)); 1223 1224 $curl->set_option('CURLOPT_USERAGENT', 'AnotherUserAgent/1.1'); 1225 $options = $curl->get_options(); 1226 $curl->call_apply_opt($options); 1227 $this->assertTrue(in_array('User-Agent: AnotherUserAgent/1.1', $curl->header)); 1228 $this->assertFalse(in_array('User-Agent: AnotherUserAgent/1.0', $curl->header)); 1229 1230 $curl->unset_option('CURLOPT_USERAGENT'); 1231 $curl->call_apply_opt(); 1232 $this->assertTrue(in_array("User-Agent: $moodlebot", $curl->header)); 1233 1234 // Finally, test it via exttests, to ensure the agent is sent properly. 1235 // Matching. 1236 $testurl = $this->getExternalTestFileUrl('/test_agent.php'); 1237 $extcurl = new \curl(); 1238 $contents = $extcurl->get($testurl, array(), array('CURLOPT_USERAGENT' => 'AnotherUserAgent/1.2')); 1239 $response = $extcurl->getResponse(); 1240 $this->assertSame('200 OK', reset($response)); 1241 $this->assertSame(0, $extcurl->get_errno()); 1242 $this->assertSame('OK', $contents); 1243 // Not matching. 1244 $contents = $extcurl->get($testurl, array(), array('CURLOPT_USERAGENT' => 'NonMatchingUserAgent/1.2')); 1245 $response = $extcurl->getResponse(); 1246 $this->assertSame('200 OK', reset($response)); 1247 $this->assertSame(0, $extcurl->get_errno()); 1248 $this->assertSame('', $contents); 1249 } 1250 1251 /** 1252 * Test file_rewrite_pluginfile_urls. 1253 */ 1254 public function test_file_rewrite_pluginfile_urls() { 1255 1256 $syscontext = \context_system::instance(); 1257 $originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">'; 1258 1259 // Do the rewrite. 1260 $finaltext = file_rewrite_pluginfile_urls($originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0); 1261 $this->assertStringContainsString("pluginfile.php", $finaltext); 1262 1263 // Now undo. 1264 $options = array('reverse' => true); 1265 $finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1266 1267 // Compare the final text is the same that the original. 1268 $this->assertEquals($originaltext, $finaltext); 1269 } 1270 1271 /** 1272 * Test file_rewrite_pluginfile_urls with includetoken. 1273 */ 1274 public function test_file_rewrite_pluginfile_urls_includetoken() { 1275 global $USER, $CFG; 1276 1277 $CFG->slasharguments = true; 1278 1279 $this->resetAfterTest(); 1280 1281 $syscontext = \context_system::instance(); 1282 $originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">'; 1283 $options = ['includetoken' => true]; 1284 1285 // Rewrite the content. This will generate a new token. 1286 $finaltext = file_rewrite_pluginfile_urls( 1287 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1288 1289 $token = get_user_key('core_files', $USER->id); 1290 $expectedurl = new \moodle_url("/tokenpluginfile.php/{$token}/{$syscontext->id}/user/private/0/image.png"); 1291 $expectedtext = "Fake test with an image <img src=\"{$expectedurl}\">"; 1292 $this->assertEquals($expectedtext, $finaltext); 1293 1294 // Do it again - the second time will use an existing token. 1295 $finaltext = file_rewrite_pluginfile_urls( 1296 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1297 $this->assertEquals($expectedtext, $finaltext); 1298 1299 // Now undo. 1300 $options['reverse'] = true; 1301 $finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1302 1303 // Compare the final text is the same that the original. 1304 $this->assertEquals($originaltext, $finaltext); 1305 1306 // Now indicates a user different than $USER. 1307 $user = $this->getDataGenerator()->create_user(); 1308 $options = ['includetoken' => $user->id]; 1309 1310 // Rewrite the content. This will generate a new token. 1311 $finaltext = file_rewrite_pluginfile_urls( 1312 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1313 1314 $token = get_user_key('core_files', $user->id); 1315 $expectedurl = new \moodle_url("/tokenpluginfile.php/{$token}/{$syscontext->id}/user/private/0/image.png"); 1316 $expectedtext = "Fake test with an image <img src=\"{$expectedurl}\">"; 1317 $this->assertEquals($expectedtext, $finaltext); 1318 } 1319 1320 /** 1321 * Test file_rewrite_pluginfile_urls with includetoken with slasharguments disabled.. 1322 */ 1323 public function test_file_rewrite_pluginfile_urls_includetoken_no_slashargs() { 1324 global $USER, $CFG; 1325 1326 $CFG->slasharguments = false; 1327 1328 $this->resetAfterTest(); 1329 1330 $syscontext = \context_system::instance(); 1331 $originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">'; 1332 $options = ['includetoken' => true]; 1333 1334 // Rewrite the content. This will generate a new token. 1335 $finaltext = file_rewrite_pluginfile_urls( 1336 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1337 1338 $token = get_user_key('core_files', $USER->id); 1339 $expectedurl = new \moodle_url("/tokenpluginfile.php"); 1340 $expectedurl .= "?token={$token}&file=/{$syscontext->id}/user/private/0/image.png"; 1341 $expectedtext = "Fake test with an image <img src=\"{$expectedurl}\">"; 1342 $this->assertEquals($expectedtext, $finaltext); 1343 1344 // Do it again - the second time will use an existing token. 1345 $finaltext = file_rewrite_pluginfile_urls( 1346 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1347 $this->assertEquals($expectedtext, $finaltext); 1348 1349 // Now undo. 1350 $options['reverse'] = true; 1351 $finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1352 1353 // Compare the final text is the same that the original. 1354 $this->assertEquals($originaltext, $finaltext); 1355 } 1356 1357 /** 1358 * Helpter function to create draft files 1359 * 1360 * @param array $filedata data for the file record (to not use defaults) 1361 * @return stored_file the stored file instance 1362 */ 1363 public static function create_draft_file($filedata = array()) { 1364 global $USER; 1365 1366 $fs = get_file_storage(); 1367 1368 $filerecord = array( 1369 'component' => 'user', 1370 'filearea' => 'draft', 1371 'itemid' => isset($filedata['itemid']) ? $filedata['itemid'] : file_get_unused_draft_itemid(), 1372 'author' => isset($filedata['author']) ? $filedata['author'] : fullname($USER), 1373 'filepath' => isset($filedata['filepath']) ? $filedata['filepath'] : '/', 1374 'filename' => isset($filedata['filename']) ? $filedata['filename'] : 'file.txt', 1375 ); 1376 1377 if (isset($filedata['contextid'])) { 1378 $filerecord['contextid'] = $filedata['contextid']; 1379 } else { 1380 $usercontext = \context_user::instance($USER->id); 1381 $filerecord['contextid'] = $usercontext->id; 1382 } 1383 $source = isset($filedata['source']) ? $filedata['source'] : serialize((object)array('source' => 'From string')); 1384 $content = isset($filedata['content']) ? $filedata['content'] : 'some content here'; 1385 1386 $file = $fs->create_file_from_string($filerecord, $content); 1387 $file->set_source($source); 1388 1389 return $file; 1390 } 1391 1392 /** 1393 * Test file_merge_files_from_draft_area_into_filearea 1394 */ 1395 public function test_file_merge_files_from_draft_area_into_filearea() { 1396 global $USER, $CFG; 1397 1398 $this->resetAfterTest(true); 1399 $this->setAdminUser(); 1400 $fs = get_file_storage(); 1401 $usercontext = \context_user::instance($USER->id); 1402 1403 // Create a draft file. 1404 $filename = 'data.txt'; 1405 $filerecord = array( 1406 'filename' => $filename, 1407 ); 1408 $file = self::create_draft_file($filerecord); 1409 $draftitemid = $file->get_itemid(); 1410 1411 $maxbytes = $CFG->userquota; 1412 $maxareabytes = $CFG->userquota; 1413 $options = array('subdirs' => 1, 1414 'maxbytes' => $maxbytes, 1415 'maxfiles' => -1, 1416 'areamaxbytes' => $maxareabytes); 1417 1418 // Add new file. 1419 file_merge_files_from_draft_area_into_filearea($draftitemid, $usercontext->id, 'user', 'private', 0, $options); 1420 1421 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1422 // Directory and file. 1423 $this->assertCount(2, $files); 1424 $found = false; 1425 foreach ($files as $file) { 1426 if (!$file->is_directory()) { 1427 $found = true; 1428 $this->assertEquals($filename, $file->get_filename()); 1429 $this->assertEquals('some content here', $file->get_content()); 1430 } 1431 } 1432 $this->assertTrue($found); 1433 1434 // Add two more files. 1435 $filerecord = array( 1436 'itemid' => $draftitemid, 1437 'filename' => 'second.txt', 1438 ); 1439 self::create_draft_file($filerecord); 1440 $filerecord = array( 1441 'itemid' => $draftitemid, 1442 'filename' => 'third.txt', 1443 ); 1444 $file = self::create_draft_file($filerecord); 1445 1446 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options); 1447 1448 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1449 $this->assertCount(4, $files); 1450 1451 // Update contents of one file. 1452 $filerecord = array( 1453 'filename' => 'second.txt', 1454 'content' => 'new content', 1455 ); 1456 $file = self::create_draft_file($filerecord); 1457 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options); 1458 1459 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1460 $this->assertCount(4, $files); 1461 $found = false; 1462 foreach ($files as $file) { 1463 if ($file->get_filename() == 'second.txt') { 1464 $found = true; 1465 $this->assertEquals('new content', $file->get_content()); 1466 } 1467 } 1468 $this->assertTrue($found); 1469 1470 // Update author. 1471 // Set different author in the current file. 1472 foreach ($files as $file) { 1473 if ($file->get_filename() == 'second.txt') { 1474 $file->set_author('Nobody'); 1475 } 1476 } 1477 $filerecord = array( 1478 'filename' => 'second.txt', 1479 ); 1480 $file = self::create_draft_file($filerecord); 1481 1482 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options); 1483 1484 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1485 $this->assertCount(4, $files); 1486 $found = false; 1487 foreach ($files as $file) { 1488 if ($file->get_filename() == 'second.txt') { 1489 $found = true; 1490 $this->assertEquals(fullname($USER), $file->get_author()); 1491 } 1492 } 1493 $this->assertTrue($found); 1494 1495 } 1496 1497 /** 1498 * Test max area bytes for file_merge_files_from_draft_area_into_filearea 1499 */ 1500 public function test_file_merge_files_from_draft_area_into_filearea_max_area_bytes() { 1501 global $USER; 1502 1503 $this->resetAfterTest(true); 1504 $this->setAdminUser(); 1505 $fs = get_file_storage(); 1506 1507 $file = self::create_draft_file(); 1508 $options = array('subdirs' => 1, 1509 'maxbytes' => 5, 1510 'maxfiles' => -1, 1511 'areamaxbytes' => 10); 1512 1513 // Add new file. 1514 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options); 1515 $usercontext = \context_user::instance($USER->id); 1516 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1517 $this->assertCount(0, $files); 1518 } 1519 1520 /** 1521 * Test max file bytes for file_merge_files_from_draft_area_into_filearea 1522 */ 1523 public function test_file_merge_files_from_draft_area_into_filearea_max_file_bytes() { 1524 global $USER; 1525 1526 $this->resetAfterTest(true); 1527 // The admin has no restriction for max file uploads, so use a normal user. 1528 $user = $this->getDataGenerator()->create_user(); 1529 $this->setUser($user); 1530 $fs = get_file_storage(); 1531 1532 $file = self::create_draft_file(); 1533 $options = array('subdirs' => 1, 1534 'maxbytes' => 1, 1535 'maxfiles' => -1, 1536 'areamaxbytes' => 100); 1537 1538 // Add new file. 1539 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options); 1540 $usercontext = \context_user::instance($USER->id); 1541 // Check we only get the base directory, not a new file. 1542 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1543 $this->assertCount(1, $files); 1544 $file = array_shift($files); 1545 $this->assertTrue($file->is_directory()); 1546 } 1547 1548 /** 1549 * Test max file number for file_merge_files_from_draft_area_into_filearea 1550 */ 1551 public function test_file_merge_files_from_draft_area_into_filearea_max_files() { 1552 global $USER; 1553 1554 $this->resetAfterTest(true); 1555 $this->setAdminUser(); 1556 $fs = get_file_storage(); 1557 1558 $file = self::create_draft_file(); 1559 $options = array('subdirs' => 1, 1560 'maxbytes' => 1000, 1561 'maxfiles' => 0, 1562 'areamaxbytes' => 1000); 1563 1564 // Add new file. 1565 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options); 1566 $usercontext = \context_user::instance($USER->id); 1567 // Check we only get the base directory, not a new file. 1568 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1569 $this->assertCount(1, $files); 1570 $file = array_shift($files); 1571 $this->assertTrue($file->is_directory()); 1572 } 1573 1574 /** 1575 * Test file_get_draft_area_info. 1576 */ 1577 public function test_file_get_draft_area_info() { 1578 global $USER; 1579 1580 $this->resetAfterTest(true); 1581 $this->setAdminUser(); 1582 $fs = get_file_storage(); 1583 1584 $filerecord = array( 1585 'filename' => 'one.txt', 1586 ); 1587 $file = self::create_draft_file($filerecord); 1588 $size = $file->get_filesize(); 1589 $draftitemid = $file->get_itemid(); 1590 // Add another file. 1591 $filerecord = array( 1592 'itemid' => $draftitemid, 1593 'filename' => 'second.txt', 1594 ); 1595 $file = self::create_draft_file($filerecord); 1596 $size += $file->get_filesize(); 1597 1598 // Create directory. 1599 $usercontext = \context_user::instance($USER->id); 1600 $dir = $fs->create_directory($usercontext->id, 'user', 'draft', $draftitemid, '/testsubdir/'); 1601 // Add file to directory. 1602 $filerecord = array( 1603 'itemid' => $draftitemid, 1604 'filename' => 'third.txt', 1605 'filepath' => '/testsubdir/', 1606 ); 1607 $file = self::create_draft_file($filerecord); 1608 $size += $file->get_filesize(); 1609 1610 $fileinfo = file_get_draft_area_info($draftitemid); 1611 $this->assertEquals(3, $fileinfo['filecount']); 1612 $this->assertEquals($size, $fileinfo['filesize']); 1613 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created. 1614 $this->assertEquals($size, $fileinfo['filesize_without_references']); 1615 1616 // Now get files from just one folder. 1617 $fileinfo = file_get_draft_area_info($draftitemid, '/testsubdir/'); 1618 $this->assertEquals(1, $fileinfo['filecount']); 1619 $this->assertEquals($file->get_filesize(), $fileinfo['filesize']); 1620 $this->assertEquals(0, $fileinfo['foldercount']); // No subdirectories inside the directory. 1621 $this->assertEquals($file->get_filesize(), $fileinfo['filesize_without_references']); 1622 1623 // Check we get the same results if we call file_get_file_area_info. 1624 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'draft', $draftitemid); 1625 $this->assertEquals(3, $fileinfo['filecount']); 1626 $this->assertEquals($size, $fileinfo['filesize']); 1627 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created. 1628 $this->assertEquals($size, $fileinfo['filesize_without_references']); 1629 } 1630 1631 /** 1632 * Test file_get_file_area_info. 1633 */ 1634 public function test_file_get_file_area_info() { 1635 global $USER; 1636 1637 $this->resetAfterTest(true); 1638 $this->setAdminUser(); 1639 $fs = get_file_storage(); 1640 1641 $filerecord = array( 1642 'filename' => 'one.txt', 1643 ); 1644 $file = self::create_draft_file($filerecord); 1645 $size = $file->get_filesize(); 1646 $draftitemid = $file->get_itemid(); 1647 // Add another file. 1648 $filerecord = array( 1649 'itemid' => $draftitemid, 1650 'filename' => 'second.txt', 1651 ); 1652 $file = self::create_draft_file($filerecord); 1653 $size += $file->get_filesize(); 1654 1655 // Create directory. 1656 $usercontext = \context_user::instance($USER->id); 1657 $dir = $fs->create_directory($usercontext->id, 'user', 'draft', $draftitemid, '/testsubdir/'); 1658 // Add file to directory. 1659 $filerecord = array( 1660 'itemid' => $draftitemid, 1661 'filename' => 'third.txt', 1662 'filepath' => '/testsubdir/', 1663 ); 1664 $file = self::create_draft_file($filerecord); 1665 $size += $file->get_filesize(); 1666 1667 // Add files to user private file area. 1668 $options = array('subdirs' => 1, 'maxfiles' => 3); 1669 file_merge_files_from_draft_area_into_filearea($draftitemid, $file->get_contextid(), 'user', 'private', 0, $options); 1670 1671 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'private'); 1672 $this->assertEquals(3, $fileinfo['filecount']); 1673 $this->assertEquals($size, $fileinfo['filesize']); 1674 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created. 1675 $this->assertEquals($size, $fileinfo['filesize_without_references']); 1676 1677 // Now get files from just one folder. 1678 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'private', 0, '/testsubdir/'); 1679 $this->assertEquals(1, $fileinfo['filecount']); 1680 $this->assertEquals($file->get_filesize(), $fileinfo['filesize']); 1681 $this->assertEquals(0, $fileinfo['foldercount']); // No subdirectories inside the directory. 1682 $this->assertEquals($file->get_filesize(), $fileinfo['filesize_without_references']); 1683 } 1684 1685 /** 1686 * Test confirming that draft files not referenced in the editor text are removed. 1687 */ 1688 public function test_file_remove_editor_orphaned_files() { 1689 global $USER, $CFG; 1690 $this->resetAfterTest(true); 1691 $this->setAdminUser(); 1692 1693 // Create three draft files. 1694 $filerecord = ['filename' => 'file1.png']; 1695 $file = self::create_draft_file($filerecord); 1696 $draftitemid = $file->get_itemid(); 1697 1698 $filerecord['itemid'] = $draftitemid; 1699 1700 $filerecord['filename'] = 'file2.png'; 1701 self::create_draft_file($filerecord); 1702 1703 $filerecord['filename'] = 'file 3.png'; 1704 self::create_draft_file($filerecord); 1705 1706 // Confirm the user drafts area lists 3 files. 1707 $fs = get_file_storage(); 1708 $usercontext = \context_user::instance($USER->id); 1709 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'itemid', 0); 1710 $this->assertCount(3, $draftfiles); 1711 1712 // Now, spoof some editor text content, referencing 2 of the files; one requiring name encoding, one not. 1713 $editor = [ 1714 'itemid' => $draftitemid, 1715 'text' => ' 1716 <img src="'.$CFG->wwwroot.'/draftfile.php/'.$usercontext->id.'/user/draft/'.$draftitemid.'/file%203.png" alt=""> 1717 <img src="'.$CFG->wwwroot.'/draftfile.php/'.$usercontext->id.'/user/draft/'.$draftitemid.'/file1.png" alt="">' 1718 ]; 1719 1720 // Run the remove orphaned drafts function and confirm that only the referenced files remain in the user drafts. 1721 $expected = ['file1.png', 'file 3.png']; // The drafts we expect will not be removed (are referenced in the online text). 1722 file_remove_editor_orphaned_files($editor); 1723 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'itemid', 0); 1724 $this->assertCount(2, $draftfiles); 1725 foreach ($draftfiles as $file) { 1726 $this->assertContains($file->get_filename(), $expected); 1727 } 1728 } 1729 1730 /** 1731 * Test that all files in the draftarea are returned. 1732 */ 1733 public function test_file_get_all_files_in_draftarea() { 1734 $this->resetAfterTest(); 1735 $this->setAdminUser(); 1736 1737 $filerecord = ['filename' => 'basepic.jpg']; 1738 $file = self::create_draft_file($filerecord); 1739 1740 $secondrecord = [ 1741 'filename' => 'infolder.jpg', 1742 'filepath' => '/assignment/', 1743 'itemid' => $file->get_itemid() 1744 ]; 1745 $file = self::create_draft_file($secondrecord); 1746 1747 $thirdrecord = [ 1748 'filename' => 'deeperfolder.jpg', 1749 'filepath' => '/assignment/pics/', 1750 'itemid' => $file->get_itemid() 1751 ]; 1752 $file = self::create_draft_file($thirdrecord); 1753 1754 $fourthrecord = [ 1755 'filename' => 'differentimage.jpg', 1756 'filepath' => '/secondfolder/', 1757 'itemid' => $file->get_itemid() 1758 ]; 1759 $file = self::create_draft_file($fourthrecord); 1760 1761 // This record has the same name as the last record, but it's in a different folder. 1762 // Just checking this is also returned. 1763 $fifthrecord = [ 1764 'filename' => 'differentimage.jpg', 1765 'filepath' => '/assignment/pics/', 1766 'itemid' => $file->get_itemid() 1767 ]; 1768 $file = self::create_draft_file($fifthrecord); 1769 1770 $allfiles = file_get_all_files_in_draftarea($file->get_itemid()); 1771 $this->assertCount(5, $allfiles); 1772 $this->assertEquals($filerecord['filename'], $allfiles[0]->filename); 1773 $this->assertEquals($secondrecord['filename'], $allfiles[1]->filename); 1774 $this->assertEquals($thirdrecord['filename'], $allfiles[2]->filename); 1775 $this->assertEquals($fourthrecord['filename'], $allfiles[3]->filename); 1776 $this->assertEquals($fifthrecord['filename'], $allfiles[4]->filename); 1777 } 1778 1779 public function test_file_copy_file_to_file_area() { 1780 // Create two files in different draft areas but owned by the same user. 1781 global $USER; 1782 $this->resetAfterTest(true); 1783 $this->setAdminUser(); 1784 1785 $filerecord = ['filename' => 'file1.png', 'itemid' => file_get_unused_draft_itemid()]; 1786 $file1 = self::create_draft_file($filerecord); 1787 $filerecord = ['filename' => 'file2.png', 'itemid' => file_get_unused_draft_itemid()]; 1788 $file2 = self::create_draft_file($filerecord); 1789 1790 // Confirm one file in each draft area. 1791 $fs = get_file_storage(); 1792 $usercontext = \context_user::instance($USER->id); 1793 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file1->get_itemid(), 'itemid', 0); 1794 $this->assertCount(1, $draftfiles); 1795 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file2->get_itemid(), 'itemid', 0); 1796 $this->assertCount(1, $draftfiles); 1797 1798 // Create file record. 1799 $filerecord = [ 1800 'component' => $file2->get_component(), 1801 'filearea' => $file2->get_filearea(), 1802 'itemid' => $file2->get_itemid(), 1803 'contextid' => $file2->get_contextid(), 1804 'filepath' => '/', 1805 'filename' => $file2->get_filename() 1806 ]; 1807 1808 // Copy file2 into file1's draft area. 1809 file_copy_file_to_file_area($filerecord, $file2->get_filename(), $file1->get_itemid()); 1810 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file1->get_itemid(), 'itemid', 0); 1811 $this->assertCount(2, $draftfiles); 1812 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file2->get_itemid(), 'itemid', 0); 1813 $this->assertCount(1, $draftfiles); 1814 } 1815 1816 /** 1817 * Test file_is_draft_areas_limit_reached 1818 */ 1819 public function test_file_is_draft_areas_limit_reached() { 1820 global $CFG; 1821 $this->resetAfterTest(true); 1822 1823 $capacity = $CFG->draft_area_bucket_capacity = 5; 1824 $leak = $CFG->draft_area_bucket_leak = 0.2; // Leaks every 5 seconds. 1825 1826 $generator = $this->getDataGenerator(); 1827 $user = $generator->create_user(); 1828 1829 $this->setUser($user); 1830 1831 $itemids = []; 1832 for ($i = 0; $i < $capacity; $i++) { 1833 $itemids[$i] = file_get_unused_draft_itemid(); 1834 } 1835 1836 // This test highly depends on time. We try to make sure that the test starts at the early moments on the second. 1837 // This was not needed if MDL-37327 was implemented. 1838 $after = time(); 1839 while (time() === $after) { 1840 usleep(100000); 1841 } 1842 1843 // Burst up to the capacity and make sure that the bucket allows it. 1844 $burststart = microtime(); 1845 for ($i = 0; $i < $capacity; $i++) { 1846 if ($i) { 1847 sleep(1); // A little delay so we have different timemodified value for files. 1848 } 1849 $this->assertFalse(file_is_draft_areas_limit_reached($user->id)); 1850 self::create_draft_file([ 1851 'filename' => 'file1.png', 1852 'itemid' => $itemids[$i], 1853 ]); 1854 } 1855 1856 // The bucket should be full after bursting. 1857 $this->assertTrue(file_is_draft_areas_limit_reached($user->id)); 1858 1859 // Calculate the time taken to burst up the bucket capacity. 1860 $timetaken = microtime_diff($burststart, microtime()); 1861 1862 // The bucket leaks so it shouldn't be full after a certain time. 1863 // Items are added into the bucket at the rate of 1 item per second. 1864 // One item leaks from the bucket every 1/$leak seconds. 1865 // So it takes 1/$leak - ($capacity-1) seconds for the bucket to leak one item and not be full anymore. 1866 $milliseconds = ceil(1000000 * ((1 / $leak) - ($capacity - 1)) - ($timetaken * 1000)); 1867 usleep($milliseconds); 1868 1869 $this->assertFalse(file_is_draft_areas_limit_reached($user->id)); 1870 1871 // Only one item was leaked from the bucket. So the bucket should become full again if we add a single item to it. 1872 self::create_draft_file([ 1873 'filename' => 'file2.png', 1874 'itemid' => $itemids[0], 1875 ]); 1876 $this->assertTrue(file_is_draft_areas_limit_reached($user->id)); 1877 1878 // The bucket leaks at a constant rate. It doesn't matter if it is filled as the result of bursting or not. 1879 sleep(ceil(1 / $leak)); 1880 $this->assertFalse(file_is_draft_areas_limit_reached($user->id)); 1881 } 1882 } 1883 1884 /** 1885 * Test-specific class to allow easier testing of curl functions. 1886 * 1887 * @copyright 2015 Dave Cooper 1888 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1889 */ 1890 class testable_curl extends curl { 1891 /** 1892 * Accessor for private options array using reflection. 1893 * 1894 * @return array 1895 */ 1896 public function get_options() { 1897 // Access to private property. 1898 $rp = new \ReflectionProperty('curl', 'options'); 1899 $rp->setAccessible(true); 1900 return $rp->getValue($this); 1901 } 1902 1903 /** 1904 * Setter for private options array using reflection. 1905 * 1906 * @param array $options 1907 */ 1908 public function set_options($options) { 1909 // Access to private property. 1910 $rp = new \ReflectionProperty('curl', 'options'); 1911 $rp->setAccessible(true); 1912 $rp->setValue($this, $options); 1913 } 1914 1915 /** 1916 * Setter for individual option. 1917 * @param string $option 1918 * @param string $value 1919 */ 1920 public function set_option($option, $value) { 1921 $options = $this->get_options(); 1922 $options[$option] = $value; 1923 $this->set_options($options); 1924 } 1925 1926 /** 1927 * Unsets an option on the curl object 1928 * @param string $option 1929 */ 1930 public function unset_option($option) { 1931 $options = $this->get_options(); 1932 unset($options[$option]); 1933 $this->set_options($options); 1934 } 1935 1936 /** 1937 * Wrapper to access the private \curl::apply_opt() method using reflection. 1938 * 1939 * @param array $options 1940 * @return resource The curl handle 1941 */ 1942 public function call_apply_opt($options = null) { 1943 // Access to private method. 1944 $rm = new \ReflectionMethod('curl', 'apply_opt'); 1945 $rm->setAccessible(true); 1946 $ch = curl_init(); 1947 return $rm->invoke($this, $ch, $options); 1948 } 1949 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body