PageRenderTime 62ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/filelib.php

https://bitbucket.org/synergylearning/campusconnect
PHP | 4641 lines | 2952 code | 496 blank | 1193 comment | 841 complexity | e15babcf76eca6bbf660528573a12a06 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, LGPL-2.1, Apache-2.0, BSD-3-Clause, AGPL-3.0

Large files files are truncated, but you can click here to view the full file

  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. * Functions for file handling.
  18. *
  19. * @package core_files
  20. * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
  21. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22. */
  23. defined('MOODLE_INTERNAL') || die();
  24. /**
  25. * BYTESERVING_BOUNDARY - string unique string constant.
  26. */
  27. define('BYTESERVING_BOUNDARY', 's1k2o3d4a5k6s7');
  28. /**
  29. * Unlimited area size constant
  30. */
  31. define('FILE_AREA_MAX_BYTES_UNLIMITED', -1);
  32. require_once("$CFG->libdir/filestorage/file_exceptions.php");
  33. require_once("$CFG->libdir/filestorage/file_storage.php");
  34. require_once("$CFG->libdir/filestorage/zip_packer.php");
  35. require_once("$CFG->libdir/filebrowser/file_browser.php");
  36. /**
  37. * Encodes file serving url
  38. *
  39. * @deprecated use moodle_url factory methods instead
  40. *
  41. * @todo MDL-31071 deprecate this function
  42. * @global stdClass $CFG
  43. * @param string $urlbase
  44. * @param string $path /filearea/itemid/dir/dir/file.exe
  45. * @param bool $forcedownload
  46. * @param bool $https https url required
  47. * @return string encoded file url
  48. */
  49. function file_encode_url($urlbase, $path, $forcedownload=false, $https=false) {
  50. global $CFG;
  51. //TODO: deprecate this
  52. if ($CFG->slasharguments) {
  53. $parts = explode('/', $path);
  54. $parts = array_map('rawurlencode', $parts);
  55. $path = implode('/', $parts);
  56. $return = $urlbase.$path;
  57. if ($forcedownload) {
  58. $return .= '?forcedownload=1';
  59. }
  60. } else {
  61. $path = rawurlencode($path);
  62. $return = $urlbase.'?file='.$path;
  63. if ($forcedownload) {
  64. $return .= '&amp;forcedownload=1';
  65. }
  66. }
  67. if ($https) {
  68. $return = str_replace('http://', 'https://', $return);
  69. }
  70. return $return;
  71. }
  72. /**
  73. * Detects if area contains subdirs,
  74. * this is intended for file areas that are attached to content
  75. * migrated from 1.x where subdirs were allowed everywhere.
  76. *
  77. * @param context $context
  78. * @param string $component
  79. * @param string $filearea
  80. * @param string $itemid
  81. * @return bool
  82. */
  83. function file_area_contains_subdirs(context $context, $component, $filearea, $itemid) {
  84. global $DB;
  85. if (!isset($itemid)) {
  86. // Not initialised yet.
  87. return false;
  88. }
  89. // Detect if any directories are already present, this is necessary for content upgraded from 1.x.
  90. $select = "contextid = :contextid AND component = :component AND filearea = :filearea AND itemid = :itemid AND filepath <> '/' AND filename = '.'";
  91. $params = array('contextid'=>$context->id, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid);
  92. return $DB->record_exists_select('files', $select, $params);
  93. }
  94. /**
  95. * Prepares 'editor' formslib element from data in database
  96. *
  97. * The passed $data record must contain field foobar, foobarformat and optionally foobartrust. This
  98. * function then copies the embedded files into draft area (assigning itemids automatically),
  99. * creates the form element foobar_editor and rewrites the URLs so the embedded images can be
  100. * displayed.
  101. * In your mform definition, you must have an 'editor' element called foobar_editor. Then you call
  102. * your mform's set_data() supplying the object returned by this function.
  103. *
  104. * @category files
  105. * @param stdClass $data database field that holds the html text with embedded media
  106. * @param string $field the name of the database field that holds the html text with embedded media
  107. * @param array $options editor options (like maxifiles, maxbytes etc.)
  108. * @param stdClass $context context of the editor
  109. * @param string $component
  110. * @param string $filearea file area name
  111. * @param int $itemid item id, required if item exists
  112. * @return stdClass modified data object
  113. */
  114. function file_prepare_standard_editor($data, $field, array $options, $context=null, $component=null, $filearea=null, $itemid=null) {
  115. $options = (array)$options;
  116. if (!isset($options['trusttext'])) {
  117. $options['trusttext'] = false;
  118. }
  119. if (!isset($options['forcehttps'])) {
  120. $options['forcehttps'] = false;
  121. }
  122. if (!isset($options['subdirs'])) {
  123. $options['subdirs'] = false;
  124. }
  125. if (!isset($options['maxfiles'])) {
  126. $options['maxfiles'] = 0; // no files by default
  127. }
  128. if (!isset($options['noclean'])) {
  129. $options['noclean'] = false;
  130. }
  131. //sanity check for passed context. This function doesn't expect $option['context'] to be set
  132. //But this function is called before creating editor hence, this is one of the best places to check
  133. //if context is used properly. This check notify developer that they missed passing context to editor.
  134. if (isset($context) && !isset($options['context'])) {
  135. //if $context is not null then make sure $option['context'] is also set.
  136. debugging('Context for editor is not set in editoroptions. Hence editor will not respect editor filters', DEBUG_DEVELOPER);
  137. } else if (isset($options['context']) && isset($context)) {
  138. //If both are passed then they should be equal.
  139. if ($options['context']->id != $context->id) {
  140. $exceptionmsg = 'Editor context ['.$options['context']->id.'] is not equal to passed context ['.$context->id.']';
  141. throw new coding_exception($exceptionmsg);
  142. }
  143. }
  144. if (is_null($itemid) or is_null($context)) {
  145. $contextid = null;
  146. $itemid = null;
  147. if (!isset($data)) {
  148. $data = new stdClass();
  149. }
  150. if (!isset($data->{$field})) {
  151. $data->{$field} = '';
  152. }
  153. if (!isset($data->{$field.'format'})) {
  154. $data->{$field.'format'} = editors_get_preferred_format();
  155. }
  156. if (!$options['noclean']) {
  157. $data->{$field} = clean_text($data->{$field}, $data->{$field.'format'});
  158. }
  159. } else {
  160. if ($options['trusttext']) {
  161. // noclean ignored if trusttext enabled
  162. if (!isset($data->{$field.'trust'})) {
  163. $data->{$field.'trust'} = 0;
  164. }
  165. $data = trusttext_pre_edit($data, $field, $context);
  166. } else {
  167. if (!$options['noclean']) {
  168. $data->{$field} = clean_text($data->{$field}, $data->{$field.'format'});
  169. }
  170. }
  171. $contextid = $context->id;
  172. }
  173. if ($options['maxfiles'] != 0) {
  174. $draftid_editor = file_get_submitted_draft_itemid($field);
  175. $currenttext = file_prepare_draft_area($draftid_editor, $contextid, $component, $filearea, $itemid, $options, $data->{$field});
  176. $data->{$field.'_editor'} = array('text'=>$currenttext, 'format'=>$data->{$field.'format'}, 'itemid'=>$draftid_editor);
  177. } else {
  178. $data->{$field.'_editor'} = array('text'=>$data->{$field}, 'format'=>$data->{$field.'format'}, 'itemid'=>0);
  179. }
  180. return $data;
  181. }
  182. /**
  183. * Prepares the content of the 'editor' form element with embedded media files to be saved in database
  184. *
  185. * This function moves files from draft area to the destination area and
  186. * encodes URLs to the draft files so they can be safely saved into DB. The
  187. * form has to contain the 'editor' element named foobar_editor, where 'foobar'
  188. * is the name of the database field to hold the wysiwyg editor content. The
  189. * editor data comes as an array with text, format and itemid properties. This
  190. * function automatically adds $data properties foobar, foobarformat and
  191. * foobartrust, where foobar has URL to embedded files encoded.
  192. *
  193. * @category files
  194. * @param stdClass $data raw data submitted by the form
  195. * @param string $field name of the database field containing the html with embedded media files
  196. * @param array $options editor options (trusttext, subdirs, maxfiles, maxbytes etc.)
  197. * @param stdClass $context context, required for existing data
  198. * @param string $component file component
  199. * @param string $filearea file area name
  200. * @param int $itemid item id, required if item exists
  201. * @return stdClass modified data object
  202. */
  203. function file_postupdate_standard_editor($data, $field, array $options, $context, $component=null, $filearea=null, $itemid=null) {
  204. $options = (array)$options;
  205. if (!isset($options['trusttext'])) {
  206. $options['trusttext'] = false;
  207. }
  208. if (!isset($options['forcehttps'])) {
  209. $options['forcehttps'] = false;
  210. }
  211. if (!isset($options['subdirs'])) {
  212. $options['subdirs'] = false;
  213. }
  214. if (!isset($options['maxfiles'])) {
  215. $options['maxfiles'] = 0; // no files by default
  216. }
  217. if (!isset($options['maxbytes'])) {
  218. $options['maxbytes'] = 0; // unlimited
  219. }
  220. if ($options['trusttext']) {
  221. $data->{$field.'trust'} = trusttext_trusted($context);
  222. } else {
  223. $data->{$field.'trust'} = 0;
  224. }
  225. $editor = $data->{$field.'_editor'};
  226. if ($options['maxfiles'] == 0 or is_null($filearea) or is_null($itemid) or empty($editor['itemid'])) {
  227. $data->{$field} = $editor['text'];
  228. } else {
  229. $data->{$field} = file_save_draft_area_files($editor['itemid'], $context->id, $component, $filearea, $itemid, $options, $editor['text'], $options['forcehttps']);
  230. }
  231. $data->{$field.'format'} = $editor['format'];
  232. return $data;
  233. }
  234. /**
  235. * Saves text and files modified by Editor formslib element
  236. *
  237. * @category files
  238. * @param stdClass $data $database entry field
  239. * @param string $field name of data field
  240. * @param array $options various options
  241. * @param stdClass $context context - must already exist
  242. * @param string $component
  243. * @param string $filearea file area name
  244. * @param int $itemid must already exist, usually means data is in db
  245. * @return stdClass modified data obejct
  246. */
  247. function file_prepare_standard_filemanager($data, $field, array $options, $context=null, $component=null, $filearea=null, $itemid=null) {
  248. $options = (array)$options;
  249. if (!isset($options['subdirs'])) {
  250. $options['subdirs'] = false;
  251. }
  252. if (is_null($itemid) or is_null($context)) {
  253. $itemid = null;
  254. $contextid = null;
  255. } else {
  256. $contextid = $context->id;
  257. }
  258. $draftid_editor = file_get_submitted_draft_itemid($field.'_filemanager');
  259. file_prepare_draft_area($draftid_editor, $contextid, $component, $filearea, $itemid, $options);
  260. $data->{$field.'_filemanager'} = $draftid_editor;
  261. return $data;
  262. }
  263. /**
  264. * Saves files modified by File manager formslib element
  265. *
  266. * @todo MDL-31073 review this function
  267. * @category files
  268. * @param stdClass $data $database entry field
  269. * @param string $field name of data field
  270. * @param array $options various options
  271. * @param stdClass $context context - must already exist
  272. * @param string $component
  273. * @param string $filearea file area name
  274. * @param int $itemid must already exist, usually means data is in db
  275. * @return stdClass modified data obejct
  276. */
  277. function file_postupdate_standard_filemanager($data, $field, array $options, $context, $component, $filearea, $itemid) {
  278. $options = (array)$options;
  279. if (!isset($options['subdirs'])) {
  280. $options['subdirs'] = false;
  281. }
  282. if (!isset($options['maxfiles'])) {
  283. $options['maxfiles'] = -1; // unlimited
  284. }
  285. if (!isset($options['maxbytes'])) {
  286. $options['maxbytes'] = 0; // unlimited
  287. }
  288. if (empty($data->{$field.'_filemanager'})) {
  289. $data->$field = '';
  290. } else {
  291. file_save_draft_area_files($data->{$field.'_filemanager'}, $context->id, $component, $filearea, $itemid, $options);
  292. $fs = get_file_storage();
  293. if ($fs->get_area_files($context->id, $component, $filearea, $itemid)) {
  294. $data->$field = '1'; // TODO: this is an ugly hack (skodak)
  295. } else {
  296. $data->$field = '';
  297. }
  298. }
  299. return $data;
  300. }
  301. /**
  302. * Generate a draft itemid
  303. *
  304. * @category files
  305. * @global moodle_database $DB
  306. * @global stdClass $USER
  307. * @return int a random but available draft itemid that can be used to create a new draft
  308. * file area.
  309. */
  310. function file_get_unused_draft_itemid() {
  311. global $DB, $USER;
  312. if (isguestuser() or !isloggedin()) {
  313. // guests and not-logged-in users can not be allowed to upload anything!!!!!!
  314. print_error('noguest');
  315. }
  316. $contextid = context_user::instance($USER->id)->id;
  317. $fs = get_file_storage();
  318. $draftitemid = rand(1, 999999999);
  319. while ($files = $fs->get_area_files($contextid, 'user', 'draft', $draftitemid)) {
  320. $draftitemid = rand(1, 999999999);
  321. }
  322. return $draftitemid;
  323. }
  324. /**
  325. * Initialise a draft file area from a real one by copying the files. A draft
  326. * area will be created if one does not already exist. Normally you should
  327. * get $draftitemid by calling file_get_submitted_draft_itemid('elementname');
  328. *
  329. * @category files
  330. * @global stdClass $CFG
  331. * @global stdClass $USER
  332. * @param int $draftitemid the id of the draft area to use, or 0 to create a new one, in which case this parameter is updated.
  333. * @param int $contextid This parameter and the next two identify the file area to copy files from.
  334. * @param string $component
  335. * @param string $filearea helps indentify the file area.
  336. * @param int $itemid helps identify the file area. Can be null if there are no files yet.
  337. * @param array $options text and file options ('subdirs'=>false, 'forcehttps'=>false)
  338. * @param string $text some html content that needs to have embedded links rewritten to point to the draft area.
  339. * @return string|null returns string if $text was passed in, the rewritten $text is returned. Otherwise NULL.
  340. */
  341. function file_prepare_draft_area(&$draftitemid, $contextid, $component, $filearea, $itemid, array $options=null, $text=null) {
  342. global $CFG, $USER, $CFG;
  343. $options = (array)$options;
  344. if (!isset($options['subdirs'])) {
  345. $options['subdirs'] = false;
  346. }
  347. if (!isset($options['forcehttps'])) {
  348. $options['forcehttps'] = false;
  349. }
  350. $usercontext = context_user::instance($USER->id);
  351. $fs = get_file_storage();
  352. if (empty($draftitemid)) {
  353. // create a new area and copy existing files into
  354. $draftitemid = file_get_unused_draft_itemid();
  355. $file_record = array('contextid'=>$usercontext->id, 'component'=>'user', 'filearea'=>'draft', 'itemid'=>$draftitemid);
  356. if (!is_null($itemid) and $files = $fs->get_area_files($contextid, $component, $filearea, $itemid)) {
  357. foreach ($files as $file) {
  358. if ($file->is_directory() and $file->get_filepath() === '/') {
  359. // we need a way to mark the age of each draft area,
  360. // by not copying the root dir we force it to be created automatically with current timestamp
  361. continue;
  362. }
  363. if (!$options['subdirs'] and ($file->is_directory() or $file->get_filepath() !== '/')) {
  364. continue;
  365. }
  366. $draftfile = $fs->create_file_from_storedfile($file_record, $file);
  367. // XXX: This is a hack for file manager (MDL-28666)
  368. // File manager needs to know the original file information before copying
  369. // to draft area, so we append these information in mdl_files.source field
  370. // {@link file_storage::search_references()}
  371. // {@link file_storage::search_references_count()}
  372. $sourcefield = $file->get_source();
  373. $newsourcefield = new stdClass;
  374. $newsourcefield->source = $sourcefield;
  375. $original = new stdClass;
  376. $original->contextid = $contextid;
  377. $original->component = $component;
  378. $original->filearea = $filearea;
  379. $original->itemid = $itemid;
  380. $original->filename = $file->get_filename();
  381. $original->filepath = $file->get_filepath();
  382. $newsourcefield->original = file_storage::pack_reference($original);
  383. $draftfile->set_source(serialize($newsourcefield));
  384. // End of file manager hack
  385. }
  386. }
  387. if (!is_null($text)) {
  388. // at this point there should not be any draftfile links yet,
  389. // because this is a new text from database that should still contain the @@pluginfile@@ links
  390. // this happens when developers forget to post process the text
  391. $text = str_replace("\"$CFG->httpswwwroot/draftfile.php", "\"$CFG->httpswwwroot/brokenfile.php#", $text);
  392. }
  393. } else {
  394. // nothing to do
  395. }
  396. if (is_null($text)) {
  397. return null;
  398. }
  399. // relink embedded files - editor can not handle @@PLUGINFILE@@ !
  400. return file_rewrite_pluginfile_urls($text, 'draftfile.php', $usercontext->id, 'user', 'draft', $draftitemid, $options);
  401. }
  402. /**
  403. * Convert encoded URLs in $text from the @@PLUGINFILE@@/... form to an actual URL.
  404. *
  405. * @category files
  406. * @global stdClass $CFG
  407. * @param string $text The content that may contain ULRs in need of rewriting.
  408. * @param string $file The script that should be used to serve these files. pluginfile.php, draftfile.php, etc.
  409. * @param int $contextid This parameter and the next two identify the file area to use.
  410. * @param string $component
  411. * @param string $filearea helps identify the file area.
  412. * @param int $itemid helps identify the file area.
  413. * @param array $options text and file options ('forcehttps'=>false)
  414. * @return string the processed text.
  415. */
  416. function file_rewrite_pluginfile_urls($text, $file, $contextid, $component, $filearea, $itemid, array $options=null) {
  417. global $CFG;
  418. $options = (array)$options;
  419. if (!isset($options['forcehttps'])) {
  420. $options['forcehttps'] = false;
  421. }
  422. if (!$CFG->slasharguments) {
  423. $file = $file . '?file=';
  424. }
  425. $baseurl = "$CFG->wwwroot/$file/$contextid/$component/$filearea/";
  426. if ($itemid !== null) {
  427. $baseurl .= "$itemid/";
  428. }
  429. if ($options['forcehttps']) {
  430. $baseurl = str_replace('http://', 'https://', $baseurl);
  431. }
  432. return str_replace('@@PLUGINFILE@@/', $baseurl, $text);
  433. }
  434. /**
  435. * Returns information about files in a draft area.
  436. *
  437. * @global stdClass $CFG
  438. * @global stdClass $USER
  439. * @param int $draftitemid the draft area item id.
  440. * @param string $filepath path to the directory from which the information have to be retrieved.
  441. * @return array with the following entries:
  442. * 'filecount' => number of files in the draft area.
  443. * 'filesize' => total size of the files in the draft area.
  444. * 'foldercount' => number of folders in the draft area.
  445. * 'filesize_without_references' => total size of the area excluding file references.
  446. * (more information will be added as needed).
  447. */
  448. function file_get_draft_area_info($draftitemid, $filepath = '/') {
  449. global $CFG, $USER;
  450. $usercontext = context_user::instance($USER->id);
  451. $fs = get_file_storage();
  452. $results = array(
  453. 'filecount' => 0,
  454. 'foldercount' => 0,
  455. 'filesize' => 0,
  456. 'filesize_without_references' => 0
  457. );
  458. if ($filepath != '/') {
  459. $draftfiles = $fs->get_directory_files($usercontext->id, 'user', 'draft', $draftitemid, $filepath, true, true);
  460. } else {
  461. $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id', true);
  462. }
  463. foreach ($draftfiles as $file) {
  464. if ($file->is_directory()) {
  465. $results['foldercount'] += 1;
  466. } else {
  467. $results['filecount'] += 1;
  468. }
  469. $filesize = $file->get_filesize();
  470. $results['filesize'] += $filesize;
  471. if (!$file->is_external_file()) {
  472. $results['filesize_without_references'] += $filesize;
  473. }
  474. }
  475. return $results;
  476. }
  477. /**
  478. * Returns whether a draft area has exceeded/will exceed its size limit.
  479. *
  480. * Please note that the unlimited value for $areamaxbytes is -1 {@link FILE_AREA_MAX_BYTES_UNLIMITED}, not 0.
  481. *
  482. * @param int $draftitemid the draft area item id.
  483. * @param int $areamaxbytes the maximum size allowed in this draft area.
  484. * @param int $newfilesize the size that would be added to the current area.
  485. * @param bool $includereferences true to include the size of the references in the area size.
  486. * @return bool true if the area will/has exceeded its limit.
  487. * @since 2.4
  488. */
  489. function file_is_draft_area_limit_reached($draftitemid, $areamaxbytes, $newfilesize = 0, $includereferences = false) {
  490. if ($areamaxbytes != FILE_AREA_MAX_BYTES_UNLIMITED) {
  491. $draftinfo = file_get_draft_area_info($draftitemid);
  492. $areasize = $draftinfo['filesize_without_references'];
  493. if ($includereferences) {
  494. $areasize = $draftinfo['filesize'];
  495. }
  496. if ($areasize + $newfilesize > $areamaxbytes) {
  497. return true;
  498. }
  499. }
  500. return false;
  501. }
  502. /**
  503. * Get used space of files
  504. * @global moodle_database $DB
  505. * @global stdClass $USER
  506. * @return int total bytes
  507. */
  508. function file_get_user_used_space() {
  509. global $DB, $USER;
  510. $usercontext = context_user::instance($USER->id);
  511. $sql = "SELECT SUM(files1.filesize) AS totalbytes FROM {files} files1
  512. JOIN (SELECT contenthash, filename, MAX(id) AS id
  513. FROM {files}
  514. WHERE contextid = ? AND component = ? AND filearea != ?
  515. GROUP BY contenthash, filename) files2 ON files1.id = files2.id";
  516. $params = array('contextid'=>$usercontext->id, 'component'=>'user', 'filearea'=>'draft');
  517. $record = $DB->get_record_sql($sql, $params);
  518. return (int)$record->totalbytes;
  519. }
  520. /**
  521. * Convert any string to a valid filepath
  522. * @todo review this function
  523. * @param string $str
  524. * @return string path
  525. */
  526. function file_correct_filepath($str) { //TODO: what is this? (skodak) - No idea (Fred)
  527. if ($str == '/' or empty($str)) {
  528. return '/';
  529. } else {
  530. return '/'.trim($str, '/').'/';
  531. }
  532. }
  533. /**
  534. * Generate a folder tree of draft area of current USER recursively
  535. *
  536. * @todo MDL-31073 use normal return value instead, this does not fit the rest of api here (skodak)
  537. * @param int $draftitemid
  538. * @param string $filepath
  539. * @param mixed $data
  540. */
  541. function file_get_drafarea_folders($draftitemid, $filepath, &$data) {
  542. global $USER, $OUTPUT, $CFG;
  543. $data->children = array();
  544. $context = context_user::instance($USER->id);
  545. $fs = get_file_storage();
  546. if ($files = $fs->get_directory_files($context->id, 'user', 'draft', $draftitemid, $filepath, false)) {
  547. foreach ($files as $file) {
  548. if ($file->is_directory()) {
  549. $item = new stdClass();
  550. $item->sortorder = $file->get_sortorder();
  551. $item->filepath = $file->get_filepath();
  552. $foldername = explode('/', trim($item->filepath, '/'));
  553. $item->fullname = trim(array_pop($foldername), '/');
  554. $item->id = uniqid();
  555. file_get_drafarea_folders($draftitemid, $item->filepath, $item);
  556. $data->children[] = $item;
  557. } else {
  558. continue;
  559. }
  560. }
  561. }
  562. }
  563. /**
  564. * Listing all files (including folders) in current path (draft area)
  565. * used by file manager
  566. * @param int $draftitemid
  567. * @param string $filepath
  568. * @return stdClass
  569. */
  570. function file_get_drafarea_files($draftitemid, $filepath = '/') {
  571. global $USER, $OUTPUT, $CFG;
  572. $context = context_user::instance($USER->id);
  573. $fs = get_file_storage();
  574. $data = new stdClass();
  575. $data->path = array();
  576. $data->path[] = array('name'=>get_string('files'), 'path'=>'/');
  577. // will be used to build breadcrumb
  578. $trail = '/';
  579. if ($filepath !== '/') {
  580. $filepath = file_correct_filepath($filepath);
  581. $parts = explode('/', $filepath);
  582. foreach ($parts as $part) {
  583. if ($part != '' && $part != null) {
  584. $trail .= ($part.'/');
  585. $data->path[] = array('name'=>$part, 'path'=>$trail);
  586. }
  587. }
  588. }
  589. $list = array();
  590. $maxlength = 12;
  591. if ($files = $fs->get_directory_files($context->id, 'user', 'draft', $draftitemid, $filepath, false)) {
  592. foreach ($files as $file) {
  593. $item = new stdClass();
  594. $item->filename = $file->get_filename();
  595. $item->filepath = $file->get_filepath();
  596. $item->fullname = trim($item->filename, '/');
  597. $filesize = $file->get_filesize();
  598. $item->size = $filesize ? $filesize : null;
  599. $item->filesize = $filesize ? display_size($filesize) : '';
  600. $item->sortorder = $file->get_sortorder();
  601. $item->author = $file->get_author();
  602. $item->license = $file->get_license();
  603. $item->datemodified = $file->get_timemodified();
  604. $item->datecreated = $file->get_timecreated();
  605. $item->isref = $file->is_external_file();
  606. if ($item->isref && $file->get_status() == 666) {
  607. $item->originalmissing = true;
  608. }
  609. // find the file this draft file was created from and count all references in local
  610. // system pointing to that file
  611. $source = @unserialize($file->get_source());
  612. if (isset($source->original)) {
  613. $item->refcount = $fs->search_references_count($source->original);
  614. }
  615. if ($file->is_directory()) {
  616. $item->filesize = 0;
  617. $item->icon = $OUTPUT->pix_url(file_folder_icon(24))->out(false);
  618. $item->type = 'folder';
  619. $foldername = explode('/', trim($item->filepath, '/'));
  620. $item->fullname = trim(array_pop($foldername), '/');
  621. $item->thumbnail = $OUTPUT->pix_url(file_folder_icon(90))->out(false);
  622. } else {
  623. // do NOT use file browser here!
  624. $item->mimetype = get_mimetype_description($file);
  625. if (file_extension_in_typegroup($file->get_filename(), 'archive')) {
  626. $item->type = 'zip';
  627. } else {
  628. $item->type = 'file';
  629. }
  630. $itemurl = moodle_url::make_draftfile_url($draftitemid, $item->filepath, $item->filename);
  631. $item->url = $itemurl->out();
  632. $item->icon = $OUTPUT->pix_url(file_file_icon($file, 24))->out(false);
  633. $item->thumbnail = $OUTPUT->pix_url(file_file_icon($file, 90))->out(false);
  634. if ($imageinfo = $file->get_imageinfo()) {
  635. $item->realthumbnail = $itemurl->out(false, array('preview' => 'thumb', 'oid' => $file->get_timemodified()));
  636. $item->realicon = $itemurl->out(false, array('preview' => 'tinyicon', 'oid' => $file->get_timemodified()));
  637. $item->image_width = $imageinfo['width'];
  638. $item->image_height = $imageinfo['height'];
  639. }
  640. }
  641. $list[] = $item;
  642. }
  643. }
  644. $data->itemid = $draftitemid;
  645. $data->list = $list;
  646. return $data;
  647. }
  648. /**
  649. * Returns draft area itemid for a given element.
  650. *
  651. * @category files
  652. * @param string $elname name of formlib editor element, or a hidden form field that stores the draft area item id, etc.
  653. * @return int the itemid, or 0 if there is not one yet.
  654. */
  655. function file_get_submitted_draft_itemid($elname) {
  656. // this is a nasty hack, ideally all new elements should use arrays here or there should be a new parameter
  657. if (!isset($_REQUEST[$elname])) {
  658. return 0;
  659. }
  660. if (is_array($_REQUEST[$elname])) {
  661. $param = optional_param_array($elname, 0, PARAM_INT);
  662. if (!empty($param['itemid'])) {
  663. $param = $param['itemid'];
  664. } else {
  665. debugging('Missing itemid, maybe caused by unset maxfiles option', DEBUG_DEVELOPER);
  666. return false;
  667. }
  668. } else {
  669. $param = optional_param($elname, 0, PARAM_INT);
  670. }
  671. if ($param) {
  672. require_sesskey();
  673. }
  674. return $param;
  675. }
  676. /**
  677. * Restore the original source field from draft files
  678. *
  679. * Do not use this function because it makes field files.source inconsistent
  680. * for draft area files. This function will be deprecated in 2.6
  681. *
  682. * @param stored_file $storedfile This only works with draft files
  683. * @return stored_file
  684. */
  685. function file_restore_source_field_from_draft_file($storedfile) {
  686. $source = @unserialize($storedfile->get_source());
  687. if (!empty($source)) {
  688. if (is_object($source)) {
  689. $restoredsource = $source->source;
  690. $storedfile->set_source($restoredsource);
  691. } else {
  692. throw new moodle_exception('invalidsourcefield', 'error');
  693. }
  694. }
  695. return $storedfile;
  696. }
  697. /**
  698. * Saves files from a draft file area to a real one (merging the list of files).
  699. * Can rewrite URLs in some content at the same time if desired.
  700. *
  701. * @category files
  702. * @global stdClass $USER
  703. * @param int $draftitemid the id of the draft area to use. Normally obtained
  704. * from file_get_submitted_draft_itemid('elementname') or similar.
  705. * @param int $contextid This parameter and the next two identify the file area to save to.
  706. * @param string $component
  707. * @param string $filearea indentifies the file area.
  708. * @param int $itemid helps identifies the file area.
  709. * @param array $options area options (subdirs=>false, maxfiles=-1, maxbytes=0)
  710. * @param string $text some html content that needs to have embedded links rewritten
  711. * to the @@PLUGINFILE@@ form for saving in the database.
  712. * @param bool $forcehttps force https urls.
  713. * @return string|null if $text was passed in, the rewritten $text is returned. Otherwise NULL.
  714. */
  715. function file_save_draft_area_files($draftitemid, $contextid, $component, $filearea, $itemid, array $options=null, $text=null, $forcehttps=false) {
  716. global $USER;
  717. $usercontext = context_user::instance($USER->id);
  718. $fs = get_file_storage();
  719. $options = (array)$options;
  720. if (!isset($options['subdirs'])) {
  721. $options['subdirs'] = false;
  722. }
  723. if (!isset($options['maxfiles'])) {
  724. $options['maxfiles'] = -1; // unlimited
  725. }
  726. if (!isset($options['maxbytes']) || $options['maxbytes'] == USER_CAN_IGNORE_FILE_SIZE_LIMITS) {
  727. $options['maxbytes'] = 0; // unlimited
  728. }
  729. if (!isset($options['areamaxbytes'])) {
  730. $options['areamaxbytes'] = FILE_AREA_MAX_BYTES_UNLIMITED; // Unlimited.
  731. }
  732. $allowreferences = true;
  733. if (isset($options['return_types']) && !($options['return_types'] & FILE_REFERENCE)) {
  734. // we assume that if $options['return_types'] is NOT specified, we DO allow references.
  735. // this is not exactly right. BUT there are many places in code where filemanager options
  736. // are not passed to file_save_draft_area_files()
  737. $allowreferences = false;
  738. }
  739. // Check if the draft area has exceeded the authorised limit. This should never happen as validation
  740. // should have taken place before, unless the user is doing something nauthly. If so, let's just not save
  741. // anything at all in the next area.
  742. if (file_is_draft_area_limit_reached($draftitemid, $options['areamaxbytes'])) {
  743. return null;
  744. }
  745. $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id');
  746. $oldfiles = $fs->get_area_files($contextid, $component, $filearea, $itemid, 'id');
  747. // One file in filearea means it is empty (it has only top-level directory '.').
  748. if (count($draftfiles) > 1 || count($oldfiles) > 1) {
  749. // we have to merge old and new files - we want to keep file ids for files that were not changed
  750. // we change time modified for all new and changed files, we keep time created as is
  751. $newhashes = array();
  752. $filecount = 0;
  753. foreach ($draftfiles as $file) {
  754. if (!$options['subdirs'] && $file->get_filepath() !== '/') {
  755. continue;
  756. }
  757. if (!$allowreferences && $file->is_external_file()) {
  758. continue;
  759. }
  760. if (!$file->is_directory()) {
  761. if ($options['maxbytes'] and $options['maxbytes'] < $file->get_filesize()) {
  762. // oversized file - should not get here at all
  763. continue;
  764. }
  765. if ($options['maxfiles'] != -1 and $options['maxfiles'] <= $filecount) {
  766. // more files - should not get here at all
  767. continue;
  768. }
  769. $filecount++;
  770. }
  771. $newhash = $fs->get_pathname_hash($contextid, $component, $filearea, $itemid, $file->get_filepath(), $file->get_filename());
  772. $newhashes[$newhash] = $file;
  773. }
  774. // Loop through oldfiles and decide which we need to delete and which to update.
  775. // After this cycle the array $newhashes will only contain the files that need to be added.
  776. foreach ($oldfiles as $oldfile) {
  777. $oldhash = $oldfile->get_pathnamehash();
  778. if (!isset($newhashes[$oldhash])) {
  779. // delete files not needed any more - deleted by user
  780. $oldfile->delete();
  781. continue;
  782. }
  783. $newfile = $newhashes[$oldhash];
  784. // Now we know that we have $oldfile and $newfile for the same path.
  785. // Let's check if we can update this file or we need to delete and create.
  786. if ($newfile->is_directory()) {
  787. // Directories are always ok to just update.
  788. } else if (($source = @unserialize($newfile->get_source())) && isset($source->original)) {
  789. // File has the 'original' - we need to update the file (it may even have not been changed at all).
  790. $original = file_storage::unpack_reference($source->original);
  791. if ($original['filename'] !== $oldfile->get_filename() || $original['filepath'] !== $oldfile->get_filepath()) {
  792. // Very odd, original points to another file. Delete and create file.
  793. $oldfile->delete();
  794. continue;
  795. }
  796. } else {
  797. // The same file name but absence of 'original' means that file was deteled and uploaded again.
  798. // By deleting and creating new file we properly manage all existing references.
  799. $oldfile->delete();
  800. continue;
  801. }
  802. // status changed, we delete old file, and create a new one
  803. if ($oldfile->get_status() != $newfile->get_status()) {
  804. // file was changed, use updated with new timemodified data
  805. $oldfile->delete();
  806. // This file will be added later
  807. continue;
  808. }
  809. // Updated author
  810. if ($oldfile->get_author() != $newfile->get_author()) {
  811. $oldfile->set_author($newfile->get_author());
  812. }
  813. // Updated license
  814. if ($oldfile->get_license() != $newfile->get_license()) {
  815. $oldfile->set_license($newfile->get_license());
  816. }
  817. // Updated file source
  818. // Field files.source for draftarea files contains serialised object with source and original information.
  819. // We only store the source part of it for non-draft file area.
  820. $newsource = $newfile->get_source();
  821. if ($source = @unserialize($newfile->get_source())) {
  822. $newsource = $source->source;
  823. }
  824. if ($oldfile->get_source() !== $newsource) {
  825. $oldfile->set_source($newsource);
  826. }
  827. // Updated sort order
  828. if ($oldfile->get_sortorder() != $newfile->get_sortorder()) {
  829. $oldfile->set_sortorder($newfile->get_sortorder());
  830. }
  831. // Update file timemodified
  832. if ($oldfile->get_timemodified() != $newfile->get_timemodified()) {
  833. $oldfile->set_timemodified($newfile->get_timemodified());
  834. }
  835. // Replaced file content
  836. if (!$oldfile->is_directory() &&
  837. ($oldfile->get_contenthash() != $newfile->get_contenthash() ||
  838. $oldfile->get_filesize() != $newfile->get_filesize() ||
  839. $oldfile->get_referencefileid() != $newfile->get_referencefileid() ||
  840. $oldfile->get_userid() != $newfile->get_userid())) {
  841. $oldfile->replace_file_with($newfile);
  842. }
  843. // unchanged file or directory - we keep it as is
  844. unset($newhashes[$oldhash]);
  845. }
  846. // Add fresh file or the file which has changed status
  847. // the size and subdirectory tests are extra safety only, the UI should prevent it
  848. foreach ($newhashes as $file) {
  849. $file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'timemodified'=>time());
  850. if ($source = @unserialize($file->get_source())) {
  851. // Field files.source for draftarea files contains serialised object with source and original information.
  852. // We only store the source part of it for non-draft file area.
  853. $file_record['source'] = $source->source;
  854. }
  855. if ($file->is_external_file()) {
  856. $repoid = $file->get_repository_id();
  857. if (!empty($repoid)) {
  858. $file_record['repositoryid'] = $repoid;
  859. $file_record['reference'] = $file->get_reference();
  860. }
  861. }
  862. $fs->create_file_from_storedfile($file_record, $file);
  863. }
  864. }
  865. // note: do not purge the draft area - we clean up areas later in cron,
  866. // the reason is that user might press submit twice and they would loose the files,
  867. // also sometimes we might want to use hacks that save files into two different areas
  868. if (is_null($text)) {
  869. return null;
  870. } else {
  871. return file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps);
  872. }
  873. }
  874. /**
  875. * Convert the draft file area URLs in some content to @@PLUGINFILE@@ tokens
  876. * ready to be saved in the database. Normally, this is done automatically by
  877. * {@link file_save_draft_area_files()}.
  878. *
  879. * @category files
  880. * @param string $text the content to process.
  881. * @param int $draftitemid the draft file area the content was using.
  882. * @param bool $forcehttps whether the content contains https URLs. Default false.
  883. * @return string the processed content.
  884. */
  885. function file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps = false) {
  886. global $CFG, $USER;
  887. $usercontext = context_user::instance($USER->id);
  888. $wwwroot = $CFG->wwwroot;
  889. if ($forcehttps) {
  890. $wwwroot = str_replace('http://', 'https://', $wwwroot);
  891. }
  892. // relink embedded files if text submitted - no absolute links allowed in database!
  893. $text = str_ireplace("$wwwroot/draftfile.php/$usercontext->id/user/draft/$draftitemid/", '@@PLUGINFILE@@/', $text);
  894. if (strpos($text, 'draftfile.php?file=') !== false) {
  895. $matches = array();
  896. preg_match_all("!$wwwroot/draftfile.php\?file=%2F{$usercontext->id}%2Fuser%2Fdraft%2F{$draftitemid}%2F[^'\",&<>|`\s:\\\\]+!iu", $text, $matches);
  897. if ($matches) {
  898. foreach ($matches[0] as $match) {
  899. $replace = str_ireplace('%2F', '/', $match);
  900. $text = str_replace($match, $replace, $text);
  901. }
  902. }
  903. $text = str_ireplace("$wwwroot/draftfile.php?file=/$usercontext->id/user/draft/$draftitemid/", '@@PLUGINFILE@@/', $text);
  904. }
  905. return $text;
  906. }
  907. /**
  908. * Set file sort order
  909. *
  910. * @global moodle_database $DB
  911. * @param int $contextid the context id
  912. * @param string $component file component
  913. * @param string $filearea file area.
  914. * @param int $itemid itemid.
  915. * @param string $filepath file path.
  916. * @param string $filename file name.
  917. * @param int $sortorder the sort order of file.
  918. * @return bool
  919. */
  920. function file_set_sortorder($contextid, $component, $filearea, $itemid, $filepath, $filename, $sortorder) {
  921. global $DB;
  922. $conditions = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'filepath'=>$filepath, 'filename'=>$filename);
  923. if ($file_record = $DB->get_record('files', $conditions)) {
  924. $sortorder = (int)$sortorder;
  925. $file_record->sortorder = $sortorder;
  926. $DB->update_record('files', $file_record);
  927. return true;
  928. }
  929. return false;
  930. }
  931. /**
  932. * reset file sort order number to 0
  933. * @global moodle_database $DB
  934. * @param int $contextid the context id
  935. * @param string $component
  936. * @param string $filearea file area.
  937. * @param int|bool $itemid itemid.
  938. * @return bool
  939. */
  940. function file_reset_sortorder($contextid, $component, $filearea, $itemid=false) {
  941. global $DB;
  942. $conditions = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea);
  943. if ($itemid !== false) {
  944. $conditions['itemid'] = $itemid;
  945. }
  946. $file_records = $DB->get_records('files', $conditions);
  947. foreach ($file_records as $file_record) {
  948. $file_record->sortorder = 0;
  949. $DB->update_record('files', $file_record);
  950. }
  951. return true;
  952. }
  953. /**
  954. * Returns description of upload error
  955. *
  956. * @param int $errorcode found in $_FILES['filename.ext']['error']
  957. * @return string error description string, '' if ok
  958. */
  959. function file_get_upload_error($errorcode) {
  960. switch ($errorcode) {
  961. case 0: // UPLOAD_ERR_OK - no error
  962. $errmessage = '';
  963. break;
  964. case 1: // UPLOAD_ERR_INI_SIZE
  965. $errmessage = get_string('uploadserverlimit');
  966. break;
  967. case 2: // UPLOAD_ERR_FORM_SIZE
  968. $errmessage = get_string('uploadformlimit');
  969. break;
  970. case 3: // UPLOAD_ERR_PARTIAL
  971. $errmessage = get_string('uploadpartialfile');
  972. break;
  973. case 4: // UPLOAD_ERR_NO_FILE
  974. $errmessage = get_string('uploadnofilefound');
  975. break;
  976. // Note: there is no error with a value of 5
  977. case 6: // UPLOAD_ERR_NO_TMP_DIR
  978. $errmessage = get_string('uploadnotempdir');
  979. break;
  980. case 7: // UPLOAD_ERR_CANT_WRITE
  981. $errmessage = get_string('uploadcantwrite');
  982. break;
  983. case 8: // UPLOAD_ERR_EXTENSION
  984. $errmessage = get_string('uploadextension');
  985. break;
  986. default:
  987. $errmessage = get_string('uploadproblem');
  988. }
  989. return $errmessage;
  990. }
  991. /**
  992. * Recursive function formating an array in POST parameter
  993. * @param array $arraydata - the array that we are going to format and add into &$data array
  994. * @param string $currentdata - a row of the final postdata array at instant T
  995. * when finish, it's assign to $data under this format: name[keyname][][]...[]='value'
  996. * @param array $data - the final data array containing all POST parameters : 1 row = 1 parameter
  997. */
  998. function format_array_postdata_for_curlcall($arraydata, $currentdata, &$data) {
  999. foreach ($arraydata as $k=>$v) {
  1000. $newcurrentdata = $currentdata;
  1001. if (is_array($v)) { //the value is an array, call the function recursively
  1002. $newcurrentdata = $newcurrentdata.'['.urlencode($k).']';
  1003. format_array_postdata_for_curlcall($v, $newcurrentdata, $data);
  1004. } else { //add the POST parameter to the $data array
  1005. $data[] = $newcurrentdata.'['.urlencode($k).']='.urlencode($v);
  1006. }
  1007. }
  1008. }
  1009. /**
  1010. * Transform a PHP array into POST parameter
  1011. * (see the recursive function format_array_postdata_for_curlcall)
  1012. * @param array $postdata
  1013. * @return array containing all POST parameters (1 row = 1 POST parameter)
  1014. */
  1015. function format_postdata_for_curlcall($postdata) {
  1016. $data = array();
  1017. foreach ($postdata as $k=>$v) {
  1018. if (is_array($v)) {
  1019. $currentdata = urlencode($k);
  1020. format_array_postdata_for_curlcall($v, $currentdata, $data);
  1021. } else {
  1022. $data[] = urlencode($k).'='.urlencode($v);
  1023. }
  1024. }
  1025. $convertedpostdata = implode('&', $data);
  1026. return $convertedpostdata;
  1027. }
  1028. /**
  1029. * Fetches content of file from Internet (using proxy if defined). Uses cURL extension if present.
  1030. * Due to security concerns only downloads from http(s) sources are supported.
  1031. *
  1032. * @category files
  1033. * @param string $url file url starting with http(s)://
  1034. * @param array $headers http headers, null if none. If set, should be an
  1035. * associative array of header name => value pairs.
  1036. * @param array $postdata array means use POST request with given parameters
  1037. * @param bool $fullresponse return headers, responses, etc in a similar way snoopy does
  1038. * (if false, just returns content)
  1039. * @param int $timeout timeout for complete download process including all file transfer
  1040. * (default 5 minutes)
  1041. * @param int $connecttimeout timeout for connection to server; this is the timeout that
  1042. * usually happens if the remote server is completely down (default 20 seconds);
  1043. * may not work when using proxy
  1044. * @param bool $skipcertverify If true, the peer's SSL certificate will not be checked.
  1045. * Only use this when already in a trusted location.
  1046. * @param string $tofile store the downloaded content to file instead of returning it.
  1047. * @param bool $calctimeout false by default, true enables an extra head request to try and determine
  1048. * filesize and appropriately larger timeout based on $CFG->curltimeoutkbitrate
  1049. * @return stdClass|string|bool stdClass object if $fullresponse is true, false if request failed, true
  1050. * if file downloaded into $tofile successfully or the file content as a string.
  1051. */
  1052. function download_file_content($url, $headers=null, $postdata=null, $fullresponse=false, $timeout=300, $connecttimeout=20, $skipcertverify=false, $tofile=NULL, $calctimeout=false) {
  1053. global $CFG;
  1054. // Only http and https links supported.
  1055. if (!preg_match('|^https?://|i', $url)) {
  1056. if ($fullresponse) {
  1057. $response = new stdClass();
  1058. $response->status = 0;
  1059. $response->headers = array();
  1060. $response->response_code = 'Invalid protocol specified in url';
  1061. $response->results = '';
  1062. $response->error = 'Invalid protocol specified in url';
  1063. return $response;
  1064. } else {
  1065. return false;
  1066. }
  1067. }
  1068. $options = array();
  1069. $headers2 = array();
  1070. if (is_array($headers)) {
  1071. foreach ($headers as $key => $value) {
  1072. if (is_numeric($key)) {
  1073. $headers2[] = $value;
  1074. } else {
  1075. $headers2[] = "$key: $value";
  1076. }
  1077. }
  1078. }
  1079. if ($skipcertverify) {
  1080. $options['CURLOPT_SSL_VERIFYPEER'] = false;
  1081. } else {
  1082. $options['CURLOPT_SSL_VERIFYPEER'] = true;
  1083. }
  1084. $options['CURLOPT_CONNECTTIMEOUT'] = $connecttimeout;
  1085. $options['CURLOPT_FOLLOWLOCATION'] = 1;
  1086. $options['CURLOPT_MAXREDIRS'] = 5;
  1087. // Use POST if requested.
  1088. if (is_array($postdata)) {
  1089. $postdata = format_postdata_for_curlcall($postdata);
  1090. } else if (empty($postdata)) {
  1091. $postdata = null;
  1092. }
  1093. // Optionally attempt to get more correct timeout by fetching the file size.
  1094. if (!isset($CFG->curltimeoutkbitrate)) {
  1095. // Use very slow rate of 56kbps as a timeout speed when not set.
  1096. $bitrate = 56;
  1097. } else {
  1098. $bitrate = $CFG->curltimeoutkbitrate;
  1099. }
  1100. if ($calctimeout and !isset($postdata)) {
  1101. $curl = new curl();
  1102. $curl->setHeader($headers2);
  1103. $curl->head($url, $postdata, $options);
  1104. $info = $curl->get_info();
  1105. $error_no = $curl->get_errno();
  1106. if (!$error_no && $info['download_content_length'] > 0) {
  1107. // No curl errors - adjust for large files only - take max timeout.
  1108. $timeout = max($timeout, ceil($info['download_content_length'] * 8 / ($bitrate * 1024)));
  1109. }
  1110. }
  1111. $curl = new curl();
  1112. $curl->setHeader($headers2);
  1113. $options['CURLOPT_RETURNTRANSFER'] = true;
  1114. $options['CURLOPT_NOBODY'] = false;
  1115. $options['CURLOPT_TIMEOUT'] = $timeout;
  1116. if ($tofile) {
  1117. $fh = fopen($tofile, 'w');
  1118. if (!$fh) {
  1119. if ($fullresponse) {
  1120. $response = new stdClass();
  1121. $response->status = 0;
  1122. $response->headers = array();
  1123. $response->response_code = 'Can not write to file';
  1124. $response->results = false;
  1125. $response->error = 'Can not write to file';
  1126. return $response;
  1127. } else {
  1128. return false;
  1129. }
  1130. }
  1131. $options['CURLOPT_FILE'] = $fh;
  1132. }
  1133. if (isset($postdata)) {
  1134. $content = $curl->post($url, $postdata, $options);
  1135. } else {
  1136. $content = $curl->get($url, null, $options);
  1137. }
  1138. if ($tofile) {
  1139. fclose($fh);
  1140. @chmod($tofile, $CFG->filepermissions);
  1141. }
  1142. /*
  1143. // Try to detect encoding problems.
  1144. if ((curl_errno($ch) == 23 or curl_errno($ch) == 61) and defined('CURLOPT_ENCODING')) {
  1145. curl_setopt($ch, CURLOPT_ENCODING, 'none');
  1146. $result = curl_exec($ch);
  1147. }
  1148. */
  1149. $info = $curl->get_info();
  1150. $error_no = $curl->get_errno();
  1151. $r

Large files files are truncated, but you can click here to view the full file