Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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 // Matching. 1258 $testurl = $this->getExternalTestFileUrl('/test_agent.php'); 1259 $extcurl = new \curl(); 1260 $contents = $extcurl->get($testurl, array(), array('CURLOPT_USERAGENT' => 'AnotherUserAgent/1.2')); 1261 $response = $extcurl->getResponse(); 1262 $this->assertSame('200 OK', reset($response)); 1263 $this->assertSame(0, $extcurl->get_errno()); 1264 $this->assertSame('OK', $contents); 1265 // Not matching. 1266 $contents = $extcurl->get($testurl, array(), array('CURLOPT_USERAGENT' => 'NonMatchingUserAgent/1.2')); 1267 $response = $extcurl->getResponse(); 1268 $this->assertSame('200 OK', reset($response)); 1269 $this->assertSame(0, $extcurl->get_errno()); 1270 $this->assertSame('', $contents); 1271 } 1272 1273 /** 1274 * Test file_rewrite_pluginfile_urls. 1275 */ 1276 public function test_file_rewrite_pluginfile_urls() { 1277 1278 $syscontext = \context_system::instance(); 1279 $originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">'; 1280 1281 // Do the rewrite. 1282 $finaltext = file_rewrite_pluginfile_urls($originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0); 1283 $this->assertStringContainsString("pluginfile.php", $finaltext); 1284 1285 // Now undo. 1286 $options = array('reverse' => true); 1287 $finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1288 1289 // Compare the final text is the same that the original. 1290 $this->assertEquals($originaltext, $finaltext); 1291 } 1292 1293 /** 1294 * Test file_rewrite_pluginfile_urls with includetoken. 1295 */ 1296 public function test_file_rewrite_pluginfile_urls_includetoken() { 1297 global $USER, $CFG; 1298 1299 $CFG->slasharguments = true; 1300 1301 $this->resetAfterTest(); 1302 1303 $syscontext = \context_system::instance(); 1304 $originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">'; 1305 $options = ['includetoken' => true]; 1306 1307 // Rewrite the content. This will generate a new token. 1308 $finaltext = file_rewrite_pluginfile_urls( 1309 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1310 1311 $token = get_user_key('core_files', $USER->id); 1312 $expectedurl = new \moodle_url("/tokenpluginfile.php/{$token}/{$syscontext->id}/user/private/0/image.png"); 1313 $expectedtext = "Fake test with an image <img src=\"{$expectedurl}\">"; 1314 $this->assertEquals($expectedtext, $finaltext); 1315 1316 // Do it again - the second time will use an existing token. 1317 $finaltext = file_rewrite_pluginfile_urls( 1318 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1319 $this->assertEquals($expectedtext, $finaltext); 1320 1321 // Now undo. 1322 $options['reverse'] = true; 1323 $finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1324 1325 // Compare the final text is the same that the original. 1326 $this->assertEquals($originaltext, $finaltext); 1327 1328 // Now indicates a user different than $USER. 1329 $user = $this->getDataGenerator()->create_user(); 1330 $options = ['includetoken' => $user->id]; 1331 1332 // Rewrite the content. This will generate a new token. 1333 $finaltext = file_rewrite_pluginfile_urls( 1334 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1335 1336 $token = get_user_key('core_files', $user->id); 1337 $expectedurl = new \moodle_url("/tokenpluginfile.php/{$token}/{$syscontext->id}/user/private/0/image.png"); 1338 $expectedtext = "Fake test with an image <img src=\"{$expectedurl}\">"; 1339 $this->assertEquals($expectedtext, $finaltext); 1340 } 1341 1342 /** 1343 * Test file_rewrite_pluginfile_urls with includetoken with slasharguments disabled.. 1344 */ 1345 public function test_file_rewrite_pluginfile_urls_includetoken_no_slashargs() { 1346 global $USER, $CFG; 1347 1348 $CFG->slasharguments = false; 1349 1350 $this->resetAfterTest(); 1351 1352 $syscontext = \context_system::instance(); 1353 $originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">'; 1354 $options = ['includetoken' => true]; 1355 1356 // Rewrite the content. This will generate a new token. 1357 $finaltext = file_rewrite_pluginfile_urls( 1358 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1359 1360 $token = get_user_key('core_files', $USER->id); 1361 $expectedurl = new \moodle_url("/tokenpluginfile.php"); 1362 $expectedurl .= "?token={$token}&file=/{$syscontext->id}/user/private/0/image.png"; 1363 $expectedtext = "Fake test with an image <img src=\"{$expectedurl}\">"; 1364 $this->assertEquals($expectedtext, $finaltext); 1365 1366 // Do it again - the second time will use an existing token. 1367 $finaltext = file_rewrite_pluginfile_urls( 1368 $originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1369 $this->assertEquals($expectedtext, $finaltext); 1370 1371 // Now undo. 1372 $options['reverse'] = true; 1373 $finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options); 1374 1375 // Compare the final text is the same that the original. 1376 $this->assertEquals($originaltext, $finaltext); 1377 } 1378 1379 /** 1380 * Helpter function to create draft files 1381 * 1382 * @param array $filedata data for the file record (to not use defaults) 1383 * @return stored_file the stored file instance 1384 */ 1385 public static function create_draft_file($filedata = array()) { 1386 global $USER; 1387 1388 $fs = get_file_storage(); 1389 1390 $filerecord = array( 1391 'component' => 'user', 1392 'filearea' => 'draft', 1393 'itemid' => isset($filedata['itemid']) ? $filedata['itemid'] : file_get_unused_draft_itemid(), 1394 'author' => isset($filedata['author']) ? $filedata['author'] : fullname($USER), 1395 'filepath' => isset($filedata['filepath']) ? $filedata['filepath'] : '/', 1396 'filename' => isset($filedata['filename']) ? $filedata['filename'] : 'file.txt', 1397 ); 1398 1399 if (isset($filedata['contextid'])) { 1400 $filerecord['contextid'] = $filedata['contextid']; 1401 } else { 1402 $usercontext = \context_user::instance($USER->id); 1403 $filerecord['contextid'] = $usercontext->id; 1404 } 1405 $source = isset($filedata['source']) ? $filedata['source'] : serialize((object)array('source' => 'From string')); 1406 $content = isset($filedata['content']) ? $filedata['content'] : 'some content here'; 1407 1408 $file = $fs->create_file_from_string($filerecord, $content); 1409 $file->set_source($source); 1410 1411 return $file; 1412 } 1413 1414 /** 1415 * Test file_merge_files_from_draft_area_into_filearea 1416 */ 1417 public function test_file_merge_files_from_draft_area_into_filearea() { 1418 global $USER, $CFG; 1419 1420 $this->resetAfterTest(true); 1421 $this->setAdminUser(); 1422 $fs = get_file_storage(); 1423 $usercontext = \context_user::instance($USER->id); 1424 1425 // Create a draft file. 1426 $filename = 'data.txt'; 1427 $filerecord = array( 1428 'filename' => $filename, 1429 ); 1430 $file = self::create_draft_file($filerecord); 1431 $draftitemid = $file->get_itemid(); 1432 1433 $maxbytes = $CFG->userquota; 1434 $maxareabytes = $CFG->userquota; 1435 $options = array('subdirs' => 1, 1436 'maxbytes' => $maxbytes, 1437 'maxfiles' => -1, 1438 'areamaxbytes' => $maxareabytes); 1439 1440 // Add new file. 1441 file_merge_files_from_draft_area_into_filearea($draftitemid, $usercontext->id, 'user', 'private', 0, $options); 1442 1443 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1444 // Directory and file. 1445 $this->assertCount(2, $files); 1446 $found = false; 1447 foreach ($files as $file) { 1448 if (!$file->is_directory()) { 1449 $found = true; 1450 $this->assertEquals($filename, $file->get_filename()); 1451 $this->assertEquals('some content here', $file->get_content()); 1452 } 1453 } 1454 $this->assertTrue($found); 1455 1456 // Add two more files. 1457 $filerecord = array( 1458 'itemid' => $draftitemid, 1459 'filename' => 'second.txt', 1460 ); 1461 self::create_draft_file($filerecord); 1462 $filerecord = array( 1463 'itemid' => $draftitemid, 1464 'filename' => 'third.txt', 1465 ); 1466 $file = self::create_draft_file($filerecord); 1467 1468 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options); 1469 1470 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1471 $this->assertCount(4, $files); 1472 1473 // Update contents of one file. 1474 $filerecord = array( 1475 'filename' => 'second.txt', 1476 'content' => 'new content', 1477 ); 1478 $file = self::create_draft_file($filerecord); 1479 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options); 1480 1481 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1482 $this->assertCount(4, $files); 1483 $found = false; 1484 foreach ($files as $file) { 1485 if ($file->get_filename() == 'second.txt') { 1486 $found = true; 1487 $this->assertEquals('new content', $file->get_content()); 1488 } 1489 } 1490 $this->assertTrue($found); 1491 1492 // Update author. 1493 // Set different author in the current file. 1494 foreach ($files as $file) { 1495 if ($file->get_filename() == 'second.txt') { 1496 $file->set_author('Nobody'); 1497 } 1498 } 1499 $filerecord = array( 1500 'filename' => 'second.txt', 1501 ); 1502 $file = self::create_draft_file($filerecord); 1503 1504 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options); 1505 1506 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1507 $this->assertCount(4, $files); 1508 $found = false; 1509 foreach ($files as $file) { 1510 if ($file->get_filename() == 'second.txt') { 1511 $found = true; 1512 $this->assertEquals(fullname($USER), $file->get_author()); 1513 } 1514 } 1515 $this->assertTrue($found); 1516 1517 } 1518 1519 /** 1520 * Test max area bytes for file_merge_files_from_draft_area_into_filearea 1521 */ 1522 public function test_file_merge_files_from_draft_area_into_filearea_max_area_bytes() { 1523 global $USER; 1524 1525 $this->resetAfterTest(true); 1526 $this->setAdminUser(); 1527 $fs = get_file_storage(); 1528 1529 $file = self::create_draft_file(); 1530 $options = array('subdirs' => 1, 1531 'maxbytes' => 5, 1532 'maxfiles' => -1, 1533 'areamaxbytes' => 10); 1534 1535 // Add new file. 1536 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options); 1537 $usercontext = \context_user::instance($USER->id); 1538 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1539 $this->assertCount(0, $files); 1540 } 1541 1542 /** 1543 * Test max file bytes for file_merge_files_from_draft_area_into_filearea 1544 */ 1545 public function test_file_merge_files_from_draft_area_into_filearea_max_file_bytes() { 1546 global $USER; 1547 1548 $this->resetAfterTest(true); 1549 // The admin has no restriction for max file uploads, so use a normal user. 1550 $user = $this->getDataGenerator()->create_user(); 1551 $this->setUser($user); 1552 $fs = get_file_storage(); 1553 1554 $file = self::create_draft_file(); 1555 $options = array('subdirs' => 1, 1556 'maxbytes' => 1, 1557 'maxfiles' => -1, 1558 'areamaxbytes' => 100); 1559 1560 // Add new file. 1561 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options); 1562 $usercontext = \context_user::instance($USER->id); 1563 // Check we only get the base directory, not a new file. 1564 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1565 $this->assertCount(1, $files); 1566 $file = array_shift($files); 1567 $this->assertTrue($file->is_directory()); 1568 } 1569 1570 /** 1571 * Test max file number for file_merge_files_from_draft_area_into_filearea 1572 */ 1573 public function test_file_merge_files_from_draft_area_into_filearea_max_files() { 1574 global $USER; 1575 1576 $this->resetAfterTest(true); 1577 $this->setAdminUser(); 1578 $fs = get_file_storage(); 1579 1580 $file = self::create_draft_file(); 1581 $options = array('subdirs' => 1, 1582 'maxbytes' => 1000, 1583 'maxfiles' => 0, 1584 'areamaxbytes' => 1000); 1585 1586 // Add new file. 1587 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options); 1588 $usercontext = \context_user::instance($USER->id); 1589 // Check we only get the base directory, not a new file. 1590 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0); 1591 $this->assertCount(1, $files); 1592 $file = array_shift($files); 1593 $this->assertTrue($file->is_directory()); 1594 } 1595 1596 /** 1597 * Test file_get_draft_area_info. 1598 */ 1599 public function test_file_get_draft_area_info() { 1600 global $USER; 1601 1602 $this->resetAfterTest(true); 1603 $this->setAdminUser(); 1604 $fs = get_file_storage(); 1605 1606 $filerecord = array( 1607 'filename' => 'one.txt', 1608 ); 1609 $file = self::create_draft_file($filerecord); 1610 $size = $file->get_filesize(); 1611 $draftitemid = $file->get_itemid(); 1612 // Add another file. 1613 $filerecord = array( 1614 'itemid' => $draftitemid, 1615 'filename' => 'second.txt', 1616 ); 1617 $file = self::create_draft_file($filerecord); 1618 $size += $file->get_filesize(); 1619 1620 // Create directory. 1621 $usercontext = \context_user::instance($USER->id); 1622 $dir = $fs->create_directory($usercontext->id, 'user', 'draft', $draftitemid, '/testsubdir/'); 1623 // Add file to directory. 1624 $filerecord = array( 1625 'itemid' => $draftitemid, 1626 'filename' => 'third.txt', 1627 'filepath' => '/testsubdir/', 1628 ); 1629 $file = self::create_draft_file($filerecord); 1630 $size += $file->get_filesize(); 1631 1632 $fileinfo = file_get_draft_area_info($draftitemid); 1633 $this->assertEquals(3, $fileinfo['filecount']); 1634 $this->assertEquals($size, $fileinfo['filesize']); 1635 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created. 1636 $this->assertEquals($size, $fileinfo['filesize_without_references']); 1637 1638 // Now get files from just one folder. 1639 $fileinfo = file_get_draft_area_info($draftitemid, '/testsubdir/'); 1640 $this->assertEquals(1, $fileinfo['filecount']); 1641 $this->assertEquals($file->get_filesize(), $fileinfo['filesize']); 1642 $this->assertEquals(0, $fileinfo['foldercount']); // No subdirectories inside the directory. 1643 $this->assertEquals($file->get_filesize(), $fileinfo['filesize_without_references']); 1644 1645 // Check we get the same results if we call file_get_file_area_info. 1646 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'draft', $draftitemid); 1647 $this->assertEquals(3, $fileinfo['filecount']); 1648 $this->assertEquals($size, $fileinfo['filesize']); 1649 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created. 1650 $this->assertEquals($size, $fileinfo['filesize_without_references']); 1651 } 1652 1653 /** 1654 * Test file_get_file_area_info. 1655 */ 1656 public function test_file_get_file_area_info() { 1657 global $USER; 1658 1659 $this->resetAfterTest(true); 1660 $this->setAdminUser(); 1661 $fs = get_file_storage(); 1662 1663 $filerecord = array( 1664 'filename' => 'one.txt', 1665 ); 1666 $file = self::create_draft_file($filerecord); 1667 $size = $file->get_filesize(); 1668 $draftitemid = $file->get_itemid(); 1669 // Add another file. 1670 $filerecord = array( 1671 'itemid' => $draftitemid, 1672 'filename' => 'second.txt', 1673 ); 1674 $file = self::create_draft_file($filerecord); 1675 $size += $file->get_filesize(); 1676 1677 // Create directory. 1678 $usercontext = \context_user::instance($USER->id); 1679 $dir = $fs->create_directory($usercontext->id, 'user', 'draft', $draftitemid, '/testsubdir/'); 1680 // Add file to directory. 1681 $filerecord = array( 1682 'itemid' => $draftitemid, 1683 'filename' => 'third.txt', 1684 'filepath' => '/testsubdir/', 1685 ); 1686 $file = self::create_draft_file($filerecord); 1687 $size += $file->get_filesize(); 1688 1689 // Add files to user private file area. 1690 $options = array('subdirs' => 1, 'maxfiles' => 3); 1691 file_merge_files_from_draft_area_into_filearea($draftitemid, $file->get_contextid(), 'user', 'private', 0, $options); 1692 1693 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'private'); 1694 $this->assertEquals(3, $fileinfo['filecount']); 1695 $this->assertEquals($size, $fileinfo['filesize']); 1696 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created. 1697 $this->assertEquals($size, $fileinfo['filesize_without_references']); 1698 1699 // Now get files from just one folder. 1700 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'private', 0, '/testsubdir/'); 1701 $this->assertEquals(1, $fileinfo['filecount']); 1702 $this->assertEquals($file->get_filesize(), $fileinfo['filesize']); 1703 $this->assertEquals(0, $fileinfo['foldercount']); // No subdirectories inside the directory. 1704 $this->assertEquals($file->get_filesize(), $fileinfo['filesize_without_references']); 1705 } 1706 1707 /** 1708 * Test confirming that draft files not referenced in the editor text are removed. 1709 */ 1710 public function test_file_remove_editor_orphaned_files() { 1711 global $USER, $CFG; 1712 $this->resetAfterTest(true); 1713 $this->setAdminUser(); 1714 1715 // Create three draft files. 1716 $filerecord = ['filename' => 'file1.png']; 1717 $file = self::create_draft_file($filerecord); 1718 $draftitemid = $file->get_itemid(); 1719 1720 $filerecord['itemid'] = $draftitemid; 1721 1722 $filerecord['filename'] = 'file2.png'; 1723 self::create_draft_file($filerecord); 1724 1725 $filerecord['filename'] = 'file 3.png'; 1726 self::create_draft_file($filerecord); 1727 1728 $filerecord['filename'] = 'file4.png'; 1729 self::create_draft_file($filerecord); 1730 1731 // Confirm the user drafts area lists 3 files. 1732 $fs = get_file_storage(); 1733 $usercontext = \context_user::instance($USER->id); 1734 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'itemid', 0); 1735 $this->assertCount(4, $draftfiles); 1736 1737 // Now, spoof some editor text content, referencing 2 of the files; one requiring name encoding, one not. 1738 $editor = [ 1739 'itemid' => $draftitemid, 1740 'text' => " 1741 <img src=\"{$CFG->wwwroot}/draftfile.php/{$usercontext->id}/user/draft/{$draftitemid}/file%203.png\" alt=\"\"> 1742 <img src=\"{$CFG->wwwroot}/draftfile.php/{$usercontext->id}/user/draft/{$draftitemid}/file1.png\" alt=\"\"> 1743 <span>{$CFG->wwwroot}/draftfile.php/{$usercontext->id}/user/draft/{$draftitemid}/file4.png</span>" 1744 ]; 1745 1746 // Run the remove orphaned drafts function and confirm that only the referenced files remain in the user drafts. 1747 // The drafts we expect will not be removed (are referenced in the online text). 1748 $expected = ['file1.png', 'file 3.png', 'file4.png']; 1749 file_remove_editor_orphaned_files($editor); 1750 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'itemid', 0); 1751 $this->assertCount(3, $draftfiles); 1752 foreach ($draftfiles as $file) { 1753 $this->assertContains($file->get_filename(), $expected); 1754 } 1755 } 1756 1757 /** 1758 * Test that all files in the draftarea are returned. 1759 */ 1760 public function test_file_get_all_files_in_draftarea() { 1761 $this->resetAfterTest(); 1762 $this->setAdminUser(); 1763 1764 $filerecord = ['filename' => 'basepic.jpg']; 1765 $file = self::create_draft_file($filerecord); 1766 1767 $secondrecord = [ 1768 'filename' => 'infolder.jpg', 1769 'filepath' => '/assignment/', 1770 'itemid' => $file->get_itemid() 1771 ]; 1772 $file = self::create_draft_file($secondrecord); 1773 1774 $thirdrecord = [ 1775 'filename' => 'deeperfolder.jpg', 1776 'filepath' => '/assignment/pics/', 1777 'itemid' => $file->get_itemid() 1778 ]; 1779 $file = self::create_draft_file($thirdrecord); 1780 1781 $fourthrecord = [ 1782 'filename' => 'differentimage.jpg', 1783 'filepath' => '/secondfolder/', 1784 'itemid' => $file->get_itemid() 1785 ]; 1786 $file = self::create_draft_file($fourthrecord); 1787 1788 // This record has the same name as the last record, but it's in a different folder. 1789 // Just checking this is also returned. 1790 $fifthrecord = [ 1791 'filename' => 'differentimage.jpg', 1792 'filepath' => '/assignment/pics/', 1793 'itemid' => $file->get_itemid() 1794 ]; 1795 $file = self::create_draft_file($fifthrecord); 1796 1797 $allfiles = file_get_all_files_in_draftarea($file->get_itemid()); 1798 $this->assertCount(5, $allfiles); 1799 $this->assertEquals($filerecord['filename'], $allfiles[0]->filename); 1800 $this->assertEquals($secondrecord['filename'], $allfiles[1]->filename); 1801 $this->assertEquals($thirdrecord['filename'], $allfiles[2]->filename); 1802 $this->assertEquals($fourthrecord['filename'], $allfiles[3]->filename); 1803 $this->assertEquals($fifthrecord['filename'], $allfiles[4]->filename); 1804 } 1805 1806 public function test_file_copy_file_to_file_area() { 1807 // Create two files in different draft areas but owned by the same user. 1808 global $USER; 1809 $this->resetAfterTest(true); 1810 $this->setAdminUser(); 1811 1812 $filerecord = ['filename' => 'file1.png', 'itemid' => file_get_unused_draft_itemid()]; 1813 $file1 = self::create_draft_file($filerecord); 1814 $filerecord = ['filename' => 'file2.png', 'itemid' => file_get_unused_draft_itemid()]; 1815 $file2 = self::create_draft_file($filerecord); 1816 1817 // Confirm one file in each draft area. 1818 $fs = get_file_storage(); 1819 $usercontext = \context_user::instance($USER->id); 1820 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file1->get_itemid(), 'itemid', 0); 1821 $this->assertCount(1, $draftfiles); 1822 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file2->get_itemid(), 'itemid', 0); 1823 $this->assertCount(1, $draftfiles); 1824 1825 // Create file record. 1826 $filerecord = [ 1827 'component' => $file2->get_component(), 1828 'filearea' => $file2->get_filearea(), 1829 'itemid' => $file2->get_itemid(), 1830 'contextid' => $file2->get_contextid(), 1831 'filepath' => '/', 1832 'filename' => $file2->get_filename() 1833 ]; 1834 1835 // Copy file2 into file1's draft area. 1836 file_copy_file_to_file_area($filerecord, $file2->get_filename(), $file1->get_itemid()); 1837 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file1->get_itemid(), 'itemid', 0); 1838 $this->assertCount(2, $draftfiles); 1839 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $file2->get_itemid(), 'itemid', 0); 1840 $this->assertCount(1, $draftfiles); 1841 } 1842 1843 /** 1844 * Test file_is_draft_areas_limit_reached 1845 */ 1846 public function test_file_is_draft_areas_limit_reached() { 1847 global $CFG; 1848 $this->resetAfterTest(true); 1849 1850 $capacity = $CFG->draft_area_bucket_capacity = 5; 1851 $leak = $CFG->draft_area_bucket_leak = 0.2; // Leaks every 5 seconds. 1852 1853 $generator = $this->getDataGenerator(); 1854 $user = $generator->create_user(); 1855 1856 $this->setUser($user); 1857 1858 $itemids = []; 1859 for ($i = 0; $i < $capacity; $i++) { 1860 $itemids[$i] = file_get_unused_draft_itemid(); 1861 } 1862 1863 // This test highly depends on time. We try to make sure that the test starts at the early moments on the second. 1864 // This was not needed if MDL-37327 was implemented. 1865 $after = time(); 1866 while (time() === $after) { 1867 usleep(100000); 1868 } 1869 1870 // Burst up to the capacity and make sure that the bucket allows it. 1871 $burststart = microtime(); 1872 for ($i = 0; $i < $capacity; $i++) { 1873 if ($i) { 1874 sleep(1); // A little delay so we have different timemodified value for files. 1875 } 1876 $this->assertFalse(file_is_draft_areas_limit_reached($user->id)); 1877 self::create_draft_file([ 1878 'filename' => 'file1.png', 1879 'itemid' => $itemids[$i], 1880 ]); 1881 } 1882 1883 // The bucket should be full after bursting. 1884 $this->assertTrue(file_is_draft_areas_limit_reached($user->id)); 1885 1886 // Calculate the time taken to burst up the bucket capacity. 1887 $timetaken = microtime_diff($burststart, microtime()); 1888 1889 // The bucket leaks so it shouldn't be full after a certain time. 1890 // Items are added into the bucket at the rate of 1 item per second. 1891 // One item leaks from the bucket every 1/$leak seconds. 1892 // So it takes 1/$leak - ($capacity-1) seconds for the bucket to leak one item and not be full anymore. 1893 $milliseconds = ceil(1000000 * ((1 / $leak) - ($capacity - 1)) - ($timetaken * 1000)); 1894 usleep($milliseconds); 1895 1896 $this->assertFalse(file_is_draft_areas_limit_reached($user->id)); 1897 1898 // Only one item was leaked from the bucket. So the bucket should become full again if we add a single item to it. 1899 self::create_draft_file([ 1900 'filename' => 'file2.png', 1901 'itemid' => $itemids[0], 1902 ]); 1903 $this->assertTrue(file_is_draft_areas_limit_reached($user->id)); 1904 1905 // The bucket leaks at a constant rate. It doesn't matter if it is filled as the result of bursting or not. 1906 sleep(ceil(1 / $leak)); 1907 $this->assertFalse(file_is_draft_areas_limit_reached($user->id)); 1908 } 1909 } 1910 1911 /** 1912 * Test-specific class to allow easier testing of curl functions. 1913 * 1914 * @copyright 2015 Dave Cooper 1915 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1916 */ 1917 class testable_curl extends curl { 1918 /** 1919 * Accessor for private options array using reflection. 1920 * 1921 * @return array 1922 */ 1923 public function get_options() { 1924 // Access to private property. 1925 $rp = new \ReflectionProperty('curl', 'options'); 1926 $rp->setAccessible(true); 1927 return $rp->getValue($this); 1928 } 1929 1930 /** 1931 * Setter for private options array using reflection. 1932 * 1933 * @param array $options 1934 */ 1935 public function set_options($options) { 1936 // Access to private property. 1937 $rp = new \ReflectionProperty('curl', 'options'); 1938 $rp->setAccessible(true); 1939 $rp->setValue($this, $options); 1940 } 1941 1942 /** 1943 * Setter for individual option. 1944 * @param string $option 1945 * @param string $value 1946 */ 1947 public function set_option($option, $value) { 1948 $options = $this->get_options(); 1949 $options[$option] = $value; 1950 $this->set_options($options); 1951 } 1952 1953 /** 1954 * Unsets an option on the curl object 1955 * @param string $option 1956 */ 1957 public function unset_option($option) { 1958 $options = $this->get_options(); 1959 unset($options[$option]); 1960 $this->set_options($options); 1961 } 1962 1963 /** 1964 * Wrapper to access the private \curl::apply_opt() method using reflection. 1965 * 1966 * @param array $options 1967 * @return resource The curl handle 1968 */ 1969 public function call_apply_opt($options = null) { 1970 // Access to private method. 1971 $rm = new \ReflectionMethod('curl', 'apply_opt'); 1972 $rm->setAccessible(true); 1973 $ch = curl_init(); 1974 return $rm->invoke($this, $ch, $options); 1975 } 1976 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body