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