MDL-49497 curl: keep api private and test it using reflection.
[moodle.git] / lib / tests / filelib_test.php
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/>.
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  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->libdir . '/filelib.php');
30 require_once($CFG->dirroot . '/repository/lib.php');
32 class core_filelib_testcase extends advanced_testcase {
33     public function test_format_postdata_for_curlcall() {
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);
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);
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);
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);
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     }
83     public function test_download_file_content() {
84         global $CFG;
86         // Test http success first.
87         $testhtml = $this->getExternalTestFileUrl('/test.html');
89         $contents = download_file_content($testhtml);
90         $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
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);
100         $result = download_file_content($testhtml, null, null, false, 300, 20, false, null, true);
101         $this->assertSame($contents, $result);
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);
111         // Test https success.
112         $testhtml = $this->getExternalTestFileUrl('/test.html', true);
114         $contents = download_file_content($testhtml, null, null, false, 300, 20, true);
115         $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
117         $contents = download_file_content($testhtml);
118         $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
120         // Now 404.
121         $testhtml = $this->getExternalTestFileUrl('/test.html_nonexistent');
123         $contents = download_file_content($testhtml);
124         $this->assertFalse($contents);
125         $this->assertDebuggingCalled();
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);
135         // Invalid url.
136         $testhtml = $this->getExternalTestFileUrl('/test.html');
137         $testhtml = str_replace('http://', 'ftp://', $testhtml);
139         $contents = download_file_content($testhtml);
140         $this->assertFalse($contents);
142         // Test standard redirects.
143         $testurl = $this->getExternalTestFileUrl('/test_redir.php');
145         $contents = download_file_content("$testurl?redir=2");
146         $this->assertSame('done', $contents);
148         $response = download_file_content("$testurl?redir=2", null, null, true);
149         $this->assertInstanceOf('stdClass', $response);
150         $this->assertSame('200', $response->status);
151         $this->assertTrue(is_array($response->headers));
152         $this->assertRegExp('|^HTTP/1\.[01] 200 OK$|', rtrim($response->response_code));
153         $this->assertSame('done', $response->results);
154         $this->assertSame('', $response->error);
156         // Commented out this block if there are performance problems.
157         /*
158         $contents = download_file_content("$testurl?redir=6");
159         $this->assertFalse(false, $contents);
160         $this->assertDebuggingCalled();
161         $response = download_file_content("$testurl?redir=6", null, null, true);
162         $this->assertInstanceOf('stdClass', $response);
163         $this->assertSame('0', $response->status);
164         $this->assertTrue(is_array($response->headers));
165         $this->assertFalse($response->results);
166         $this->assertNotEmpty($response->error);
167         */
169         // Test relative redirects.
170         $testurl = $this->getExternalTestFileUrl('/test_relative_redir.php');
172         $contents = download_file_content("$testurl");
173         $this->assertSame('done', $contents);
175         $contents = download_file_content("$testurl?unused=xxx");
176         $this->assertSame('done', $contents);
177     }
179     /**
180      * Test curl basics.
181      */
182     public function test_curl_basics() {
183         global $CFG;
185         // Test HTTP success.
186         $testhtml = $this->getExternalTestFileUrl('/test.html');
188         $curl = new curl();
189         $contents = $curl->get($testhtml);
190         $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
191         $this->assertSame(0, $curl->get_errno());
193         $curl = new curl();
194         $tofile = "$CFG->tempdir/test.html";
195         @unlink($tofile);
196         $fp = fopen($tofile, 'w');
197         $result = $curl->get($testhtml, array(), array('CURLOPT_FILE'=>$fp));
198         $this->assertTrue($result);
199         fclose($fp);
200         $this->assertFileExists($tofile);
201         $this->assertSame($contents, file_get_contents($tofile));
202         @unlink($tofile);
204         $curl = new curl();
205         $tofile = "$CFG->tempdir/test.html";
206         @unlink($tofile);
207         $result = $curl->download_one($testhtml, array(), array('filepath'=>$tofile));
208         $this->assertTrue($result);
209         $this->assertFileExists($tofile);
210         $this->assertSame($contents, file_get_contents($tofile));
211         @unlink($tofile);
213         // Test 404 request.
214         $curl = new curl();
215         $contents = $curl->get($this->getExternalTestFileUrl('/i.do.not.exist'));
216         $response = $curl->getResponse();
217         $this->assertSame('404 Not Found', reset($response));
218         $this->assertSame(0, $curl->get_errno());
219     }
221     public function test_curl_redirects() {
222         global $CFG;
224         // Test full URL redirects.
225         $testurl = $this->getExternalTestFileUrl('/test_redir.php');
227         $curl = new curl();
228         $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>2));
229         $response = $curl->getResponse();
230         $this->assertSame('200 OK', reset($response));
231         $this->assertSame(0, $curl->get_errno());
232         $this->assertSame(2, $curl->info['redirect_count']);
233         $this->assertSame('done', $contents);
235         $curl = new curl();
236         $curl->emulateredirects = true;
237         $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>2));
238         $response = $curl->getResponse();
239         $this->assertSame('200 OK', reset($response));
240         $this->assertSame(0, $curl->get_errno());
241         $this->assertSame(2, $curl->info['redirect_count']);
242         $this->assertSame('done', $contents);
244         // This test was failing for people behind Squid proxies. Squid does not
245         // fully support HTTP 1.1, so converts things to HTTP 1.0, where the name
246         // of the status code is different.
247         reset($response);
248         if (key($response) === 'HTTP/1.0') {
249             $responsecode302 = '302 Moved Temporarily';
250         } else {
251             $responsecode302 = '302 Found';
252         }
254         $curl = new curl();
255         $contents = $curl->get("$testurl?redir=3", array(), array('CURLOPT_FOLLOWLOCATION'=>0));
256         $response = $curl->getResponse();
257         $this->assertSame($responsecode302, reset($response));
258         $this->assertSame(0, $curl->get_errno());
259         $this->assertSame(302, $curl->info['http_code']);
260         $this->assertSame('', $contents);
262         $curl = new curl();
263         $curl->emulateredirects = true;
264         $contents = $curl->get("$testurl?redir=3", array(), array('CURLOPT_FOLLOWLOCATION'=>0));
265         $response = $curl->getResponse();
266         $this->assertSame($responsecode302, reset($response));
267         $this->assertSame(0, $curl->get_errno());
268         $this->assertSame(302, $curl->info['http_code']);
269         $this->assertSame('', $contents);
271         $curl = new curl();
272         $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>1));
273         $this->assertSame(CURLE_TOO_MANY_REDIRECTS, $curl->get_errno());
274         $this->assertNotEmpty($contents);
276         $curl = new curl();
277         $curl->emulateredirects = true;
278         $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>1));
279         $this->assertSame(CURLE_TOO_MANY_REDIRECTS, $curl->get_errno());
280         $this->assertNotEmpty($contents);
282         $curl = new curl();
283         $tofile = "$CFG->tempdir/test.html";
284         @unlink($tofile);
285         $fp = fopen($tofile, 'w');
286         $result = $curl->get("$testurl?redir=1", array(), array('CURLOPT_FILE'=>$fp));
287         $this->assertTrue($result);
288         fclose($fp);
289         $this->assertFileExists($tofile);
290         $this->assertSame('done', file_get_contents($tofile));
291         @unlink($tofile);
293         $curl = new curl();
294         $curl->emulateredirects = true;
295         $tofile = "$CFG->tempdir/test.html";
296         @unlink($tofile);
297         $fp = fopen($tofile, 'w');
298         $result = $curl->get("$testurl?redir=1", array(), array('CURLOPT_FILE'=>$fp));
299         $this->assertTrue($result);
300         fclose($fp);
301         $this->assertFileExists($tofile);
302         $this->assertSame('done', file_get_contents($tofile));
303         @unlink($tofile);
305         $curl = new curl();
306         $tofile = "$CFG->tempdir/test.html";
307         @unlink($tofile);
308         $result = $curl->download_one("$testurl?redir=1", array(), array('filepath'=>$tofile));
309         $this->assertTrue($result);
310         $this->assertFileExists($tofile);
311         $this->assertSame('done', file_get_contents($tofile));
312         @unlink($tofile);
314         $curl = new curl();
315         $curl->emulateredirects = true;
316         $tofile = "$CFG->tempdir/test.html";
317         @unlink($tofile);
318         $result = $curl->download_one("$testurl?redir=1", array(), array('filepath'=>$tofile));
319         $this->assertTrue($result);
320         $this->assertFileExists($tofile);
321         $this->assertSame('done', file_get_contents($tofile));
322         @unlink($tofile);
323     }
325     public function test_curl_relative_redirects() {
326         // Test relative location redirects.
327         $testurl = $this->getExternalTestFileUrl('/test_relative_redir.php');
329         $curl = new curl();
330         $contents = $curl->get($testurl);
331         $response = $curl->getResponse();
332         $this->assertSame('200 OK', reset($response));
333         $this->assertSame(0, $curl->get_errno());
334         $this->assertSame(1, $curl->info['redirect_count']);
335         $this->assertSame('done', $contents);
337         $curl = new curl();
338         $curl->emulateredirects = true;
339         $contents = $curl->get($testurl);
340         $response = $curl->getResponse();
341         $this->assertSame('200 OK', reset($response));
342         $this->assertSame(0, $curl->get_errno());
343         $this->assertSame(1, $curl->info['redirect_count']);
344         $this->assertSame('done', $contents);
346         // Test different redirect types.
347         $testurl = $this->getExternalTestFileUrl('/test_relative_redir.php');
349         $curl = new curl();
350         $contents = $curl->get("$testurl?type=301");
351         $response = $curl->getResponse();
352         $this->assertSame('200 OK', reset($response));
353         $this->assertSame(0, $curl->get_errno());
354         $this->assertSame(1, $curl->info['redirect_count']);
355         $this->assertSame('done', $contents);
357         $curl = new curl();
358         $curl->emulateredirects = true;
359         $contents = $curl->get("$testurl?type=301");
360         $response = $curl->getResponse();
361         $this->assertSame('200 OK', reset($response));
362         $this->assertSame(0, $curl->get_errno());
363         $this->assertSame(1, $curl->info['redirect_count']);
364         $this->assertSame('done', $contents);
366         $curl = new curl();
367         $contents = $curl->get("$testurl?type=302");
368         $response = $curl->getResponse();
369         $this->assertSame('200 OK', reset($response));
370         $this->assertSame(0, $curl->get_errno());
371         $this->assertSame(1, $curl->info['redirect_count']);
372         $this->assertSame('done', $contents);
374         $curl = new curl();
375         $curl->emulateredirects = true;
376         $contents = $curl->get("$testurl?type=302");
377         $response = $curl->getResponse();
378         $this->assertSame('200 OK', reset($response));
379         $this->assertSame(0, $curl->get_errno());
380         $this->assertSame(1, $curl->info['redirect_count']);
381         $this->assertSame('done', $contents);
383         $curl = new curl();
384         $contents = $curl->get("$testurl?type=303");
385         $response = $curl->getResponse();
386         $this->assertSame('200 OK', reset($response));
387         $this->assertSame(0, $curl->get_errno());
388         $this->assertSame(1, $curl->info['redirect_count']);
389         $this->assertSame('done', $contents);
391         $curl = new curl();
392         $curl->emulateredirects = true;
393         $contents = $curl->get("$testurl?type=303");
394         $response = $curl->getResponse();
395         $this->assertSame('200 OK', reset($response));
396         $this->assertSame(0, $curl->get_errno());
397         $this->assertSame(1, $curl->info['redirect_count']);
398         $this->assertSame('done', $contents);
400         $curl = new curl();
401         $contents = $curl->get("$testurl?type=307");
402         $response = $curl->getResponse();
403         $this->assertSame('200 OK', reset($response));
404         $this->assertSame(0, $curl->get_errno());
405         $this->assertSame(1, $curl->info['redirect_count']);
406         $this->assertSame('done', $contents);
408         $curl = new curl();
409         $curl->emulateredirects = true;
410         $contents = $curl->get("$testurl?type=307");
411         $response = $curl->getResponse();
412         $this->assertSame('200 OK', reset($response));
413         $this->assertSame(0, $curl->get_errno());
414         $this->assertSame(1, $curl->info['redirect_count']);
415         $this->assertSame('done', $contents);
417         $curl = new curl();
418         $contents = $curl->get("$testurl?type=308");
419         $response = $curl->getResponse();
420         $this->assertSame('200 OK', reset($response));
421         $this->assertSame(0, $curl->get_errno());
422         $this->assertSame(1, $curl->info['redirect_count']);
423         $this->assertSame('done', $contents);
425         $curl = new curl();
426         $curl->emulateredirects = true;
427         $contents = $curl->get("$testurl?type=308");
428         $response = $curl->getResponse();
429         $this->assertSame('200 OK', reset($response));
430         $this->assertSame(0, $curl->get_errno());
431         $this->assertSame(1, $curl->info['redirect_count']);
432         $this->assertSame('done', $contents);
434     }
436     public function test_curl_proxybypass() {
437         global $CFG;
438         $testurl = $this->getExternalTestFileUrl('/test.html');
440         $oldproxy = $CFG->proxyhost;
441         $oldproxybypass = $CFG->proxybypass;
443         // Test without proxy bypass and inaccessible proxy.
444         $CFG->proxyhost = 'i.do.not.exist';
445         $CFG->proxybypass = '';
446         $curl = new curl();
447         $contents = $curl->get($testurl);
448         $this->assertNotEquals(0, $curl->get_errno());
449         $this->assertNotEquals('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
451         // Test with proxy bypass.
452         $testurlhost = parse_url($testurl, PHP_URL_HOST);
453         $CFG->proxybypass = $testurlhost;
454         $curl = new curl();
455         $contents = $curl->get($testurl);
456         $this->assertSame(0, $curl->get_errno());
457         $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
459         $CFG->proxyhost = $oldproxy;
460         $CFG->proxybypass = $oldproxybypass;
461     }
463     public function test_curl_post() {
464         $testurl = $this->getExternalTestFileUrl('/test_post.php');
466         // Test post request.
467         $curl = new curl();
468         $contents = $curl->post($testurl, 'data=moodletest');
469         $response = $curl->getResponse();
470         $this->assertSame('200 OK', reset($response));
471         $this->assertSame(0, $curl->get_errno());
472         $this->assertSame('OK', $contents);
474         // Test 100 requests.
475         $curl = new curl();
476         $curl->setHeader('Expect: 100-continue');
477         $contents = $curl->post($testurl, 'data=moodletest');
478         $response = $curl->getResponse();
479         $this->assertSame('200 OK', reset($response));
480         $this->assertSame(0, $curl->get_errno());
481         $this->assertSame('OK', $contents);
482     }
484     public function test_curl_file() {
485         $this->resetAfterTest();
486         $testurl = $this->getExternalTestFileUrl('/test_file.php');
488         $fs = get_file_storage();
489         $filerecord = array(
490             'contextid' => context_system::instance()->id,
491             'component' => 'test',
492             'filearea' => 'curl_post',
493             'itemid' => 0,
494             'filepath' => '/',
495             'filename' => 'test.txt'
496         );
497         $teststring = 'moodletest';
498         $testfile = $fs->create_file_from_string($filerecord, $teststring);
500         // Test post with file.
501         $data = array('testfile' => $testfile);
502         $curl = new curl();
503         $contents = $curl->post($testurl, $data);
504         $this->assertSame('OK', $contents);
505     }
507     public function test_curl_protocols() {
509         // HTTP and HTTPS requests were verified in previous requests. Now check
510         // that we can selectively disable some protocols.
511         $curl = new curl();
513         // Other protocols than HTTP(S) are disabled by default.
514         $testurl = 'file:///';
515         $curl->get($testurl);
516         $this->assertNotEmpty($curl->error);
517         $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
519         $testurl = 'ftp://nowhere';
520         $curl->get($testurl);
521         $this->assertNotEmpty($curl->error);
522         $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
524         $testurl = 'telnet://somewhere';
525         $curl->get($testurl);
526         $this->assertNotEmpty($curl->error);
527         $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
529         // Protocols are also disabled during redirections.
530         $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
531         $curl->get($testurl, array('proto' => 'file'));
532         $this->assertNotEmpty($curl->error);
533         $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
535         $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
536         $curl->get($testurl, array('proto' => 'ftp'));
537         $this->assertNotEmpty($curl->error);
538         $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
540         $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
541         $curl->get($testurl, array('proto' => 'telnet'));
542         $this->assertNotEmpty($curl->error);
543         $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
544     }
546     /**
547      * Testing prepare draft area
548      *
549      * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
550      * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
551      */
552     public function test_prepare_draft_area() {
553         global $USER, $DB;
555         $this->resetAfterTest(true);
557         $generator = $this->getDataGenerator();
558         $user = $generator->create_user();
559         $usercontext = context_user::instance($user->id);
560         $USER = $DB->get_record('user', array('id'=>$user->id));
562         $repositorypluginname = 'user';
564         $args = array();
565         $args['type'] = $repositorypluginname;
566         $repos = repository::get_instances($args);
567         $userrepository = reset($repos);
568         $this->assertInstanceOf('repository', $userrepository);
570         $fs = get_file_storage();
572         $syscontext = context_system::instance();
573         $component = 'core';
574         $filearea  = 'unittest';
575         $itemid    = 0;
576         $filepath  = '/';
577         $filename  = 'test.txt';
578         $sourcefield = 'Copyright stuff';
580         $filerecord = array(
581             'contextid' => $syscontext->id,
582             'component' => $component,
583             'filearea'  => $filearea,
584             'itemid'    => $itemid,
585             'filepath'  => $filepath,
586             'filename'  => $filename,
587             'source'    => $sourcefield,
588         );
589         $ref = $fs->pack_reference($filerecord);
590         $originalfile = $fs->create_file_from_string($filerecord, 'Test content');
591         $fileid = $originalfile->get_id();
592         $this->assertInstanceOf('stored_file', $originalfile);
594         // Create a user private file.
595         $userfilerecord = new stdClass;
596         $userfilerecord->contextid = $usercontext->id;
597         $userfilerecord->component = 'user';
598         $userfilerecord->filearea  = 'private';
599         $userfilerecord->itemid    = 0;
600         $userfilerecord->filepath  = '/';
601         $userfilerecord->filename  = 'userfile.txt';
602         $userfilerecord->source    = 'test';
603         $userfile = $fs->create_file_from_string($userfilerecord, 'User file content');
604         $userfileref = $fs->pack_reference($userfilerecord);
606         $filerefrecord = clone((object)$filerecord);
607         $filerefrecord->filename = 'testref.txt';
609         // Create a file reference.
610         $fileref = $fs->create_file_from_reference($filerefrecord, $userrepository->id, $userfileref);
611         $this->assertInstanceOf('stored_file', $fileref);
612         $this->assertEquals($userrepository->id, $fileref->get_repository_id());
613         $this->assertSame($userfile->get_contenthash(), $fileref->get_contenthash());
614         $this->assertEquals($userfile->get_filesize(), $fileref->get_filesize());
615         $this->assertRegExp('#' . $userfile->get_filename(). '$#', $fileref->get_reference_details());
617         $draftitemid = 0;
618         file_prepare_draft_area($draftitemid, $syscontext->id, $component, $filearea, $itemid);
620         $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid);
621         $this->assertCount(3, $draftfiles);
623         $draftfile = $fs->get_file($usercontext->id, 'user', 'draft', $draftitemid, $filepath, $filename);
624         $source = unserialize($draftfile->get_source());
625         $this->assertSame($ref, $source->original);
626         $this->assertSame($sourcefield, $source->source);
628         $draftfileref = $fs->get_file($usercontext->id, 'user', 'draft', $draftitemid, $filepath, $filerefrecord->filename);
629         $this->assertInstanceOf('stored_file', $draftfileref);
630         $this->assertTrue($draftfileref->is_external_file());
632         // Change some information.
633         $author = 'Dongsheng Cai';
634         $draftfile->set_author($author);
635         $newsourcefield = 'Get from Flickr';
636         $license = 'GPLv3';
637         $draftfile->set_license($license);
638         // If you want to really just change source field, do this.
639         $source = unserialize($draftfile->get_source());
640         $newsourcefield = 'From flickr';
641         $source->source = $newsourcefield;
642         $draftfile->set_source(serialize($source));
644         // Save changed file.
645         file_save_draft_area_files($draftitemid, $syscontext->id, $component, $filearea, $itemid);
647         $file = $fs->get_file($syscontext->id, $component, $filearea, $itemid, $filepath, $filename);
649         // Make sure it's the original file id.
650         $this->assertEquals($fileid, $file->get_id());
651         $this->assertInstanceOf('stored_file', $file);
652         $this->assertSame($author, $file->get_author());
653         $this->assertSame($license, $file->get_license());
654         $this->assertEquals($newsourcefield, $file->get_source());
655     }
657     /**
658      * Testing deleting original files.
659      *
660      * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
661      * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
662      */
663     public function test_delete_original_file_from_draft() {
664         global $USER, $DB;
666         $this->resetAfterTest(true);
668         $generator = $this->getDataGenerator();
669         $user = $generator->create_user();
670         $usercontext = context_user::instance($user->id);
671         $USER = $DB->get_record('user', array('id'=>$user->id));
673         $repositorypluginname = 'user';
675         $args = array();
676         $args['type'] = $repositorypluginname;
677         $repos = repository::get_instances($args);
678         $userrepository = reset($repos);
679         $this->assertInstanceOf('repository', $userrepository);
681         $fs = get_file_storage();
682         $syscontext = context_system::instance();
684         $filecontent = 'User file content';
686         // Create a user private file.
687         $userfilerecord = new stdClass;
688         $userfilerecord->contextid = $usercontext->id;
689         $userfilerecord->component = 'user';
690         $userfilerecord->filearea  = 'private';
691         $userfilerecord->itemid    = 0;
692         $userfilerecord->filepath  = '/';
693         $userfilerecord->filename  = 'userfile.txt';
694         $userfilerecord->source    = 'test';
695         $userfile = $fs->create_file_from_string($userfilerecord, $filecontent);
696         $userfileref = $fs->pack_reference($userfilerecord);
697         $contenthash = $userfile->get_contenthash();
699         $filerecord = array(
700             'contextid' => $syscontext->id,
701             'component' => 'core',
702             'filearea'  => 'phpunit',
703             'itemid'    => 0,
704             'filepath'  => '/',
705             'filename'  => 'test.txt',
706         );
707         // Create a file reference.
708         $fileref = $fs->create_file_from_reference($filerecord, $userrepository->id, $userfileref);
709         $this->assertInstanceOf('stored_file', $fileref);
710         $this->assertEquals($userrepository->id, $fileref->get_repository_id());
711         $this->assertSame($userfile->get_contenthash(), $fileref->get_contenthash());
712         $this->assertEquals($userfile->get_filesize(), $fileref->get_filesize());
713         $this->assertRegExp('#' . $userfile->get_filename(). '$#', $fileref->get_reference_details());
715         $draftitemid = 0;
716         file_prepare_draft_area($draftitemid, $usercontext->id, 'user', 'private', 0);
717         $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid);
718         $this->assertCount(2, $draftfiles);
719         $draftfile = $fs->get_file($usercontext->id, 'user', 'draft', $draftitemid, $userfilerecord->filepath, $userfilerecord->filename);
720         $draftfile->delete();
721         // Save changed file.
722         file_save_draft_area_files($draftitemid, $usercontext->id, 'user', 'private', 0);
724         // The file reference should be a regular moodle file now.
725         $fileref = $fs->get_file($syscontext->id, 'core', 'phpunit', 0, '/', 'test.txt');
726         $this->assertFalse($fileref->is_external_file());
727         $this->assertSame($contenthash, $fileref->get_contenthash());
728         $this->assertEquals($filecontent, $fileref->get_content());
729     }
731     /**
732      * Tests the strip_double_headers function in the curl class.
733      */
734     public function test_curl_strip_double_headers() {
735         // Example from issue tracker.
736         $mdl30648example = <<<EOF
737 HTTP/1.0 407 Proxy Authentication Required
738 Server: squid/2.7.STABLE9
739 Date: Thu, 08 Dec 2011 14:44:33 GMT
740 Content-Type: text/html
741 Content-Length: 1275
742 X-Squid-Error: ERR_CACHE_ACCESS_DENIED 0
743 Proxy-Authenticate: Basic realm="Squid proxy-caching web server"
744 X-Cache: MISS from homer.lancs.ac.uk
745 X-Cache-Lookup: NONE from homer.lancs.ac.uk:3128
746 Via: 1.0 homer.lancs.ac.uk:3128 (squid/2.7.STABLE9)
747 Connection: close
749 HTTP/1.0 200 OK
750 Server: Apache
751 X-Lb-Nocache: true
752 Cache-Control: private, max-age=15, no-transform
753 ETag: "4d69af5d8ba873ea9192c489e151bd7b"
754 Content-Type: text/html
755 Date: Thu, 08 Dec 2011 14:44:53 GMT
756 Set-Cookie: BBC-UID=c4de2e109c8df6a51de627cee11b214bd4fb6054a030222488317afb31b343360MoodleBot/1.0; expires=Mon, 07-Dec-15 14:44:53 GMT; path=/; domain=bbc.co.uk
757 X-Cache-Action: MISS
758 X-Cache-Age: 0
759 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
760 X-Cache: MISS from ww
762 <html>...
763 EOF;
764         $mdl30648expected = <<<EOF
765 HTTP/1.0 200 OK
766 Server: Apache
767 X-Lb-Nocache: true
768 Cache-Control: private, max-age=15, no-transform
769 ETag: "4d69af5d8ba873ea9192c489e151bd7b"
770 Content-Type: text/html
771 Date: Thu, 08 Dec 2011 14:44:53 GMT
772 Set-Cookie: BBC-UID=c4de2e109c8df6a51de627cee11b214bd4fb6054a030222488317afb31b343360MoodleBot/1.0; expires=Mon, 07-Dec-15 14:44:53 GMT; path=/; domain=bbc.co.uk
773 X-Cache-Action: MISS
774 X-Cache-Age: 0
775 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
776 X-Cache: MISS from ww
778 <html>...
779 EOF;
780         // For HTTP, replace the \n with \r\n.
781         $mdl30648example = preg_replace("~(?!<\r)\n~", "\r\n", $mdl30648example);
782         $mdl30648expected = preg_replace("~(?!<\r)\n~", "\r\n", $mdl30648expected);
784         // Test stripping works OK.
785         $this->assertSame($mdl30648expected, curl::strip_double_headers($mdl30648example));
786         // Test it does nothing to the 'plain' data.
787         $this->assertSame($mdl30648expected, curl::strip_double_headers($mdl30648expected));
789         // Example from OU proxy.
790         $httpsexample = <<<EOF
791 HTTP/1.0 200 Connection established
793 HTTP/1.1 200 OK
794 Date: Fri, 22 Feb 2013 17:14:23 GMT
795 Server: Apache/2
796 X-Powered-By: PHP/5.3.3-7+squeeze14
797 Content-Type: text/xml
798 Connection: close
799 Content-Encoding: gzip
800 Transfer-Encoding: chunked
802 <?xml version="1.0" encoding="ISO-8859-1" ?>
803 <rss version="2.0">...
804 EOF;
805         $httpsexpected = <<<EOF
806 HTTP/1.1 200 OK
807 Date: Fri, 22 Feb 2013 17:14:23 GMT
808 Server: Apache/2
809 X-Powered-By: PHP/5.3.3-7+squeeze14
810 Content-Type: text/xml
811 Connection: close
812 Content-Encoding: gzip
813 Transfer-Encoding: chunked
815 <?xml version="1.0" encoding="ISO-8859-1" ?>
816 <rss version="2.0">...
817 EOF;
818         // For HTTP, replace the \n with \r\n.
819         $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample);
820         $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected);
822         // Test stripping works OK.
823         $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample));
824         // Test it does nothing to the 'plain' data.
825         $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected));
826     }
828     /**
829      * Tests the get_mimetype_description function.
830      */
831     public function test_get_mimetype_description() {
832         $this->resetAfterTest();
834         // Test example type (.doc).
835         $this->assertEquals(get_string('application/msword', 'mimetypes'),
836                 get_mimetype_description(array('filename' => 'test.doc')));
838         // Test an unknown file type.
839         $this->assertEquals(get_string('document/unknown', 'mimetypes'),
840                 get_mimetype_description(array('filename' => 'test.frog')));
842         // Test a custom filetype with no lang string specified.
843         core_filetypes::add_type('frog', 'application/x-frog', 'document');
844         $this->assertEquals('application/x-frog',
845                 get_mimetype_description(array('filename' => 'test.frog')));
847         // Test custom description.
848         core_filetypes::update_type('frog', 'frog', 'application/x-frog', 'document',
849                 array(), '', 'Froggy file');
850         $this->assertEquals('Froggy file',
851                 get_mimetype_description(array('filename' => 'test.frog')));
853         // Test custom description using multilang filter.
854         filter_set_global_state('multilang', TEXTFILTER_ON);
855         filter_set_applies_to_strings('multilang', true);
856         core_filetypes::update_type('frog', 'frog', 'application/x-frog', 'document',
857                 array(), '', '<span lang="en" class="multilang">Green amphibian</span>' .
858                 '<span lang="fr" class="multilang">Amphibian vert</span>');
859         $this->assertEquals('Green amphibian',
860                 get_mimetype_description(array('filename' => 'test.frog')));
861     }
863     /**
864      * Tests the get_mimetypes_array function.
865      */
866     public function test_get_mimetypes_array() {
867         $mimeinfo = get_mimetypes_array();
869         // Test example MIME type (doc).
870         $this->assertEquals('application/msword', $mimeinfo['doc']['type']);
871         $this->assertEquals('document', $mimeinfo['doc']['icon']);
872         $this->assertEquals(array('document'), $mimeinfo['doc']['groups']);
873         $this->assertFalse(isset($mimeinfo['doc']['string']));
874         $this->assertFalse(isset($mimeinfo['doc']['defaulticon']));
875         $this->assertFalse(isset($mimeinfo['doc']['customdescription']));
877         // Check the less common fields using other examples.
878         $this->assertEquals('image', $mimeinfo['png']['string']);
879         $this->assertEquals(true, $mimeinfo['txt']['defaulticon']);
880     }
882     /**
883      * Test curl agent settings.
884      */
885     public function test_curl_useragent() {
886         $curl = new testable_curl();
887         $options = $curl->get_options();
888         $this->assertNotEmpty($options);
890         $curl->call_apply_opt($options);
891         $this->assertTrue(in_array('User-Agent: MoodleBot/1.0', $curl->header));
892         $this->assertFalse(in_array('User-Agent: Test/1.0', $curl->header));
894         $options['CURLOPT_USERAGENT'] = 'Test/1.0';
895         $curl->call_apply_opt($options);
896         $this->assertTrue(in_array('User-Agent: Test/1.0', $curl->header));
897         $this->assertFalse(in_array('User-Agent: MoodleBot/1.0', $curl->header));
899         $curl->set_option('CURLOPT_USERAGENT', 'AnotherUserAgent/1.0');
900         $curl->call_apply_opt();
901         $this->assertTrue(in_array('User-Agent: AnotherUserAgent/1.0', $curl->header));
902         $this->assertFalse(in_array('User-Agent: Test/1.0', $curl->header));
904         $curl->set_option('CURLOPT_USERAGENT', 'AnotherUserAgent/1.1');
905         $options = $curl->get_options();
906         $curl->call_apply_opt($options);
907         $this->assertTrue(in_array('User-Agent: AnotherUserAgent/1.1', $curl->header));
908         $this->assertFalse(in_array('User-Agent: AnotherUserAgent/1.0', $curl->header));
910         $curl->unset_option('CURLOPT_USERAGENT');
911         $curl->call_apply_opt();
912         $this->assertTrue(in_array('User-Agent: MoodleBot/1.0', $curl->header));
914         // Finally, test it via exttests, to ensure the agent is sent properly.
915         // Matching.
916         $testurl = $this->getExternalTestFileUrl('/test_agent.php');
917         $extcurl = new curl();
918         $contents = $extcurl->get($testurl, array(), array('CURLOPT_USERAGENT' => 'AnotherUserAgent/1.2'));
919         $response = $extcurl->getResponse();
920         $this->assertSame('200 OK', reset($response));
921         $this->assertSame(0, $extcurl->get_errno());
922         $this->assertSame('OK', $contents);
923         // Not matching.
924         $contents = $extcurl->get($testurl, array(), array('CURLOPT_USERAGENT' => 'NonMatchingUserAgent/1.2'));
925         $response = $extcurl->getResponse();
926         $this->assertSame('200 OK', reset($response));
927         $this->assertSame(0, $extcurl->get_errno());
928         $this->assertSame('', $contents);
929     }
932 /**
933  * Test-specific class to allow easier testing of curl functions.
934  *
935  * @copyright 2015 Dave Cooper
936  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
937  */
938 class testable_curl extends curl {
939     /**
940      * Accessor for private options array using reflection.
941      *
942      * @return array
943      */
944     public function get_options() {
945         // Access to private property.
946         $rp = new ReflectionProperty('curl', 'options');
947         $rp->setAccessible(true);
948         return $rp->getValue($this);
949     }
951     /**
952      * Setter for private options array using reflection.
953      *
954      * @param array $options
955      */
956     public function set_options($options) {
957         // Access to private property.
958         $rp = new ReflectionProperty('curl', 'options');
959         $rp->setAccessible(true);
960         $rp->setValue($this, $options);
961     }
963     /**
964      * Setter for individual option.
965      * @param string $option
966      * @param string $value
967      */
968     public function set_option($option, $value) {
969         $options = $this->get_options();
970         $options[$option] = $value;
971         $this->set_options($options);
972     }
974     /**
975      * Unsets an option on the curl object
976      * @param string $option
977      */
978     public function unset_option($option) {
979         $options = $this->get_options();
980         unset($options[$option]);
981         $this->set_options($options);
982     }
984     /**
985      * Wrapper to access the private curl::apply_opt() method using reflection.
986      *
987      * @param array $options
988      * @return resource The curl handle
989      */
990     public function call_apply_opt($options = null) {
991         // Access to private method.
992         $rm = new ReflectionMethod('curl', 'apply_opt');
993         $rm->setAccessible(true);
994         $ch = curl_init();
995         return $rm->invoke($this, $ch, $options);
996     }