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