PageRenderTime 82ms CodeModel.GetById 35ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/filelib.php

https://github.com/nfreear/moodle
PHP | 3096 lines | 1979 code | 274 blank | 843 comment | 450 complexity | 2f1d16160006e497f69909fd98b6c9c5 MD5 | raw file

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

   1<?php
   2
   3// This file is part of Moodle - http://moodle.org/
   4//
   5// Moodle is free software: you can redistribute it and/or modify
   6// it under the terms of the GNU General Public License as published by
   7// the Free Software Foundation, either version 3 of the License, or
   8// (at your option) any later version.
   9//
  10// Moodle is distributed in the hope that it will be useful,
  11// but WITHOUT ANY WARRANTY; without even the implied warranty of
  12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13// GNU General Public License for more details.
  14//
  15// You should have received a copy of the GNU General Public License
  16// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17
  18/**
  19 * Functions for file handling.
  20 *
  21 * @package    core
  22 * @subpackage file
  23 * @copyright  1999 onwards Martin Dougiamas (http://dougiamas.com)
  24 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25 */
  26
  27defined('MOODLE_INTERNAL') || die();
  28
  29/** @var string unique string constant. */
  30define('BYTESERVING_BOUNDARY', 's1k2o3d4a5k6s7');
  31
  32require_once("$CFG->libdir/filestorage/file_exceptions.php");
  33require_once("$CFG->libdir/filestorage/file_storage.php");
  34require_once("$CFG->libdir/filestorage/zip_packer.php");
  35require_once("$CFG->libdir/filebrowser/file_browser.php");
  36
  37/**
  38 * Encodes file serving url
  39 *
  40 * @deprecated use moodle_url factory methods instead
  41 *
  42 * @global object
  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 */
  49function file_encode_url($urlbase, $path, $forcedownload=false, $https=false) {
  50    global $CFG;
  51
  52//TODO: deprecate this
  53
  54    if ($CFG->slasharguments) {
  55        $parts = explode('/', $path);
  56        $parts = array_map('rawurlencode', $parts);
  57        $path  = implode('/', $parts);
  58        $return = $urlbase.$path;
  59        if ($forcedownload) {
  60            $return .= '?forcedownload=1';
  61        }
  62    } else {
  63        $path = rawurlencode($path);
  64        $return = $urlbase.'?file='.$path;
  65        if ($forcedownload) {
  66            $return .= '&amp;forcedownload=1';
  67        }
  68    }
  69
  70    if ($https) {
  71        $return = str_replace('http://', 'https://', $return);
  72    }
  73
  74    return $return;
  75}
  76
  77/**
  78 * Prepares 'editor' formslib element from data in database
  79 *
  80 * The passed $data record must contain field foobar, foobarformat and optionally foobartrust. This
  81 * function then copies the embedded files into draft area (assigning itemids automatically),
  82 * creates the form element foobar_editor and rewrites the URLs so the embedded images can be
  83 * displayed.
  84 * In your mform definition, you must have an 'editor' element called foobar_editor. Then you call
  85 * your mform's set_data() supplying the object returned by this function.
  86 *
  87 * @param object $data database field that holds the html text with embedded media
  88 * @param string $field the name of the database field that holds the html text with embedded media
  89 * @param array $options editor options (like maxifiles, maxbytes etc.)
  90 * @param object $context context of the editor
  91 * @param string $component
  92 * @param string $filearea file area name
  93 * @param int $itemid item id, required if item exists
  94 * @return object modified data object
  95 */
  96function file_prepare_standard_editor($data, $field, array $options, $context=null, $component=null, $filearea=null, $itemid=null) {
  97    $options = (array)$options;
  98    if (!isset($options['trusttext'])) {
  99        $options['trusttext'] = false;
 100    }
 101    if (!isset($options['forcehttps'])) {
 102        $options['forcehttps'] = false;
 103    }
 104    if (!isset($options['subdirs'])) {
 105        $options['subdirs'] = false;
 106    }
 107    if (!isset($options['maxfiles'])) {
 108        $options['maxfiles'] = 0; // no files by default
 109    }
 110    if (!isset($options['noclean'])) {
 111        $options['noclean'] = false;
 112    }
 113
 114    if (is_null($itemid) or is_null($context)) {
 115        $contextid = null;
 116        $itemid = null;
 117        if (!isset($data->{$field})) {
 118            $data->{$field} = '';
 119        }
 120        if (!isset($data->{$field.'format'})) {
 121            $data->{$field.'format'} = editors_get_preferred_format();
 122        }
 123        if (!$options['noclean']) {
 124            $data->{$field} = clean_text($data->{$field}, $data->{$field.'format'});
 125        }
 126
 127    } else {
 128        if ($options['trusttext']) {
 129            // noclean ignored if trusttext enabled
 130            if (!isset($data->{$field.'trust'})) {
 131                $data->{$field.'trust'} = 0;
 132            }
 133            $data = trusttext_pre_edit($data, $field, $context);
 134        } else {
 135            if (!$options['noclean']) {
 136                $data->{$field} = clean_text($data->{$field}, $data->{$field.'format'});
 137            }
 138        }
 139        $contextid = $context->id;
 140    }
 141
 142    if ($options['maxfiles'] != 0) {
 143        $draftid_editor = file_get_submitted_draft_itemid($field);
 144        $currenttext = file_prepare_draft_area($draftid_editor, $contextid, $component, $filearea, $itemid, $options, $data->{$field});
 145        $data->{$field.'_editor'} = array('text'=>$currenttext, 'format'=>$data->{$field.'format'}, 'itemid'=>$draftid_editor);
 146    } else {
 147        $data->{$field.'_editor'} = array('text'=>$data->{$field}, 'format'=>$data->{$field.'format'}, 'itemid'=>0);
 148    }
 149
 150    return $data;
 151}
 152
 153/**
 154 * Prepares the content of the 'editor' form element with embedded media files to be saved in database
 155 *
 156 * This function moves files from draft area to the destination area and
 157 * encodes URLs to the draft files so they can be safely saved into DB. The
 158 * form has to contain the 'editor' element named foobar_editor, where 'foobar'
 159 * is the name of the database field to hold the wysiwyg editor content. The
 160 * editor data comes as an array with text, format and itemid properties. This
 161 * function automatically adds $data properties foobar, foobarformat and
 162 * foobartrust, where foobar has URL to embedded files encoded.
 163 *
 164 * @param object $data raw data submitted by the form
 165 * @param string $field name of the database field containing the html with embedded media files
 166 * @param array $options editor options (trusttext, subdirs, maxfiles, maxbytes etc.)
 167 * @param object $context context, required for existing data
 168 * @param string component
 169 * @param string $filearea file area name
 170 * @param int $itemid item id, required if item exists
 171 * @return object modified data object
 172 */
 173function file_postupdate_standard_editor($data, $field, array $options, $context, $component=null, $filearea=null, $itemid=null) {
 174    $options = (array)$options;
 175    if (!isset($options['trusttext'])) {
 176        $options['trusttext'] = false;
 177    }
 178    if (!isset($options['forcehttps'])) {
 179        $options['forcehttps'] = false;
 180    }
 181    if (!isset($options['subdirs'])) {
 182        $options['subdirs'] = false;
 183    }
 184    if (!isset($options['maxfiles'])) {
 185        $options['maxfiles'] = 0; // no files by default
 186    }
 187    if (!isset($options['maxbytes'])) {
 188        $options['maxbytes'] = 0; // unlimited
 189    }
 190
 191    if ($options['trusttext']) {
 192        $data->{$field.'trust'} = trusttext_trusted($context);
 193    } else {
 194        $data->{$field.'trust'} = 0;
 195    }
 196
 197    $editor = $data->{$field.'_editor'};
 198
 199    if ($options['maxfiles'] == 0 or is_null($filearea) or is_null($itemid) or empty($editor['itemid'])) {
 200        $data->{$field} = $editor['text'];
 201    } else {
 202        $data->{$field} = file_save_draft_area_files($editor['itemid'], $context->id, $component, $filearea, $itemid, $options, $editor['text'], $options['forcehttps']);
 203    }
 204    $data->{$field.'format'} = $editor['format'];
 205
 206    return $data;
 207}
 208
 209/**
 210 * Saves text and files modified by Editor formslib element
 211 *
 212 * @param object $data $database entry field
 213 * @param string $field name of data field
 214 * @param array $options various options
 215 * @param object $context context - must already exist
 216 * @param string $component
 217 * @param string $filearea file area name
 218 * @param int $itemid must already exist, usually means data is in db
 219 * @return object modified data obejct
 220 */
 221function file_prepare_standard_filemanager($data, $field, array $options, $context=null, $component=null, $filearea=null, $itemid=null) {
 222    $options = (array)$options;
 223    if (!isset($options['subdirs'])) {
 224        $options['subdirs'] = false;
 225    }
 226    if (is_null($itemid) or is_null($context)) {
 227        $itemid = null;
 228        $contextid = null;
 229    } else {
 230        $contextid = $context->id;
 231    }
 232
 233    $draftid_editor = file_get_submitted_draft_itemid($field.'_filemanager');
 234    file_prepare_draft_area($draftid_editor, $contextid, $component, $filearea, $itemid, $options);
 235    $data->{$field.'_filemanager'} = $draftid_editor;
 236
 237    return $data;
 238}
 239
 240/**
 241 * Saves files modified by File manager formslib element
 242 *
 243 * @param object $data $database entry field
 244 * @param string $field name of data field
 245 * @param array $options various options
 246 * @param object $context context - must already exist
 247 * @param string $component
 248 * @param string $filearea file area name
 249 * @param int $itemid must already exist, usually means data is in db
 250 * @return object modified data obejct
 251 */
 252function file_postupdate_standard_filemanager($data, $field, array $options, $context, $component, $filearea, $itemid) {
 253    $options = (array)$options;
 254    if (!isset($options['subdirs'])) {
 255        $options['subdirs'] = false;
 256    }
 257    if (!isset($options['maxfiles'])) {
 258        $options['maxfiles'] = -1; // unlimited
 259    }
 260    if (!isset($options['maxbytes'])) {
 261        $options['maxbytes'] = 0; // unlimited
 262    }
 263
 264    if (empty($data->{$field.'_filemanager'})) {
 265        $data->$field = '';
 266
 267    } else {
 268        file_save_draft_area_files($data->{$field.'_filemanager'}, $context->id, $component, $filearea, $itemid, $options);
 269        $fs = get_file_storage();
 270
 271        if ($fs->get_area_files($context->id, $component, $filearea, $itemid)) {
 272            $data->$field = '1'; // TODO: this is an ugly hack (skodak)
 273        } else {
 274            $data->$field = '';
 275        }
 276    }
 277
 278    return $data;
 279}
 280
 281/**
 282 *
 283 * @global object
 284 * @global object
 285 * @return int a random but available draft itemid that can be used to create a new draft
 286 * file area.
 287 */
 288function file_get_unused_draft_itemid() {
 289    global $DB, $USER;
 290
 291    if (isguestuser() or !isloggedin()) {
 292        // guests and not-logged-in users can not be allowed to upload anything!!!!!!
 293        print_error('noguest');
 294    }
 295
 296    $contextid = get_context_instance(CONTEXT_USER, $USER->id)->id;
 297
 298    $fs = get_file_storage();
 299    $draftitemid = rand(1, 999999999);
 300    while ($files = $fs->get_area_files($contextid, 'user', 'draft', $draftitemid)) {
 301        $draftitemid = rand(1, 999999999);
 302    }
 303
 304    return $draftitemid;
 305}
 306
 307/**
 308 * Initialise a draft file area from a real one by copying the files. A draft
 309 * area will be created if one does not already exist. Normally you should
 310 * get $draftitemid by calling file_get_submitted_draft_itemid('elementname');
 311 *
 312 * @global object
 313 * @global object
 314 * @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.
 315 * @param integer $contextid This parameter and the next two identify the file area to copy files from.
 316 * @param string $component
 317 * @param string $filearea helps indentify the file area.
 318 * @param integer $itemid helps identify the file area. Can be null if there are no files yet.
 319 * @param array $options text and file options ('subdirs'=>false, 'forcehttps'=>false)
 320 * @param string $text some html content that needs to have embedded links rewritten to point to the draft area.
 321 * @return string if $text was passed in, the rewritten $text is returned. Otherwise NULL.
 322 */
 323function file_prepare_draft_area(&$draftitemid, $contextid, $component, $filearea, $itemid, array $options=null, $text=null) {
 324    global $CFG, $USER, $CFG;
 325
 326    $options = (array)$options;
 327    if (!isset($options['subdirs'])) {
 328        $options['subdirs'] = false;
 329    }
 330    if (!isset($options['forcehttps'])) {
 331        $options['forcehttps'] = false;
 332    }
 333
 334    $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
 335    $fs = get_file_storage();
 336
 337    if (empty($draftitemid)) {
 338        // create a new area and copy existing files into
 339        $draftitemid = file_get_unused_draft_itemid();
 340        $file_record = array('contextid'=>$usercontext->id, 'component'=>'user', 'filearea'=>'draft', 'itemid'=>$draftitemid);
 341        if (!is_null($itemid) and $files = $fs->get_area_files($contextid, $component, $filearea, $itemid)) {
 342            foreach ($files as $file) {
 343                if ($file->is_directory() and $file->get_filepath() === '/') {
 344                    // we need a way to mark the age of each draft area,
 345                    // by not copying the root dir we force it to be created automatically with current timestamp
 346                    continue;
 347                }
 348                if (!$options['subdirs'] and ($file->is_directory() or $file->get_filepath() !== '/')) {
 349                    continue;
 350                }
 351                $fs->create_file_from_storedfile($file_record, $file);
 352            }
 353        }
 354        if (!is_null($text)) {
 355            // at this point there should not be any draftfile links yet,
 356            // because this is a new text from database that should still contain the @@pluginfile@@ links
 357            // this happens when developers forget to post process the text
 358            $text = str_replace("\"$CFG->httpswwwroot/draftfile.php", "\"$CFG->httpswwwroot/brokenfile.php#", $text);
 359        }
 360    } else {
 361        // nothing to do
 362    }
 363
 364    if (is_null($text)) {
 365        return null;
 366    }
 367
 368    // relink embedded files - editor can not handle @@PLUGINFILE@@ !
 369    return file_rewrite_pluginfile_urls($text, 'draftfile.php', $usercontext->id, 'user', 'draft', $draftitemid, $options);
 370}
 371
 372/**
 373 * Convert encoded URLs in $text from the @@PLUGINFILE@@/... form to an actual URL.
 374 *
 375 * @global object
 376 * @param string $text The content that may contain ULRs in need of rewriting.
 377 * @param string $file The script that should be used to serve these files. pluginfile.php, draftfile.php, etc.
 378 * @param integer $contextid This parameter and the next two identify the file area to use.
 379 * @param string $component
 380 * @param string $filearea helps identify the file area.
 381 * @param integer $itemid helps identify the file area.
 382 * @param array $options text and file options ('forcehttps'=>false)
 383 * @return string the processed text.
 384 */
 385function file_rewrite_pluginfile_urls($text, $file, $contextid, $component, $filearea, $itemid, array $options=null) {
 386    global $CFG;
 387
 388    $options = (array)$options;
 389    if (!isset($options['forcehttps'])) {
 390        $options['forcehttps'] = false;
 391    }
 392
 393    if (!$CFG->slasharguments) {
 394        $file = $file . '?file=';
 395    }
 396
 397    $baseurl = "$CFG->wwwroot/$file/$contextid/$component/$filearea/";
 398
 399    if ($itemid !== null) {
 400        $baseurl .= "$itemid/";
 401    }
 402
 403    if ($options['forcehttps']) {
 404        $baseurl = str_replace('http://', 'https://', $baseurl);
 405    }
 406
 407    return str_replace('@@PLUGINFILE@@/', $baseurl, $text);
 408}
 409
 410/**
 411 * Returns information about files in a draft area.
 412 *
 413 * @global object
 414 * @global object
 415 * @param integer $draftitemid the draft area item id.
 416 * @return array with the following entries:
 417 *      'filecount' => number of files in the draft area.
 418 * (more information will be added as needed).
 419 */
 420function file_get_draft_area_info($draftitemid) {
 421    global $CFG, $USER;
 422
 423    $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
 424    $fs = get_file_storage();
 425
 426    $results = array();
 427
 428    // The number of files
 429    $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id', false);
 430    $results['filecount'] = count($draftfiles);
 431    $results['filesize'] = 0;
 432    foreach ($draftfiles as $file) {
 433        $results['filesize'] += $file->get_filesize();
 434    }
 435
 436    return $results;
 437}
 438
 439/**
 440 * Get used space of files
 441 * @return int total bytes
 442 */
 443function file_get_user_used_space() {
 444    global $DB, $USER;
 445
 446    $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
 447    $sql = "SELECT SUM(files1.filesize) AS totalbytes FROM {files} files1
 448            JOIN (SELECT contenthash, filename, MAX(id) AS id
 449            FROM {files}
 450            WHERE contextid = ? AND component = ? AND filearea != ?
 451            GROUP BY contenthash, filename) files2 ON files1.id = files2.id";
 452    $params = array('contextid'=>$usercontext->id, 'component'=>'user', 'filearea'=>'draft');
 453    $record = $DB->get_record_sql($sql, $params);
 454    return (int)$record->totalbytes;
 455}
 456
 457/**
 458 * Convert any string to a valid filepath
 459 * @param string $str
 460 * @return string path
 461 */
 462function file_correct_filepath($str) { //TODO: what is this? (skodak)
 463    if ($str == '/' or empty($str)) {
 464        return '/';
 465    } else {
 466        return '/'.trim($str, './@#$ ').'/';
 467    }
 468}
 469
 470/**
 471 * Generate a folder tree of draft area of current USER recursively
 472 * @param int $itemid
 473 * @param string $filepath
 474 * @param mixed $data //TODO: use normal return value instead, this does not fit the rest of api here (skodak)
 475 */
 476function file_get_drafarea_folders($draftitemid, $filepath, &$data) {
 477    global $USER, $OUTPUT, $CFG;
 478    $data->children = array();
 479    $context = get_context_instance(CONTEXT_USER, $USER->id);
 480    $fs = get_file_storage();
 481    if ($files = $fs->get_directory_files($context->id, 'user', 'draft', $draftitemid, $filepath, false)) {
 482        foreach ($files as $file) {
 483            if ($file->is_directory()) {
 484                $item = new stdClass();
 485                $item->sortorder = $file->get_sortorder();
 486                $item->filepath = $file->get_filepath();
 487
 488                $foldername = explode('/', trim($item->filepath, '/'));
 489                $item->fullname = trim(array_pop($foldername), '/');
 490
 491                $item->id = uniqid();
 492                file_get_drafarea_folders($draftitemid, $item->filepath, $item);
 493                $data->children[] = $item;
 494            } else {
 495                continue;
 496            }
 497        }
 498    }
 499}
 500
 501/**
 502 * Listing all files (including folders) in current path (draft area)
 503 * used by file manager
 504 * @param int $draftitemid
 505 * @param string $filepath
 506 * @return mixed
 507 */
 508function file_get_drafarea_files($draftitemid, $filepath = '/') {
 509    global $USER, $OUTPUT, $CFG;
 510
 511    $context = get_context_instance(CONTEXT_USER, $USER->id);
 512    $fs = get_file_storage();
 513
 514    $data = new stdClass();
 515    $data->path = array();
 516    $data->path[] = array('name'=>get_string('files'), 'path'=>'/');
 517
 518    // will be used to build breadcrumb
 519    $trail = '';
 520    if ($filepath !== '/') {
 521        $filepath = file_correct_filepath($filepath);
 522        $parts = explode('/', $filepath);
 523        foreach ($parts as $part) {
 524            if ($part != '' && $part != null) {
 525                $trail .= ('/'.$part.'/');
 526                $data->path[] = array('name'=>$part, 'path'=>$trail);
 527            }
 528        }
 529    }
 530
 531    $list = array();
 532    $maxlength = 12;
 533    if ($files = $fs->get_directory_files($context->id, 'user', 'draft', $draftitemid, $filepath, false)) {
 534        foreach ($files as $file) {
 535            $item = new stdClass();
 536            $item->filename = $file->get_filename();
 537            $item->filepath = $file->get_filepath();
 538            $item->fullname = trim($item->filename, '/');
 539            $filesize = $file->get_filesize();
 540            $item->filesize = $filesize ? display_size($filesize) : '';
 541
 542            $icon = mimeinfo_from_type('icon', $file->get_mimetype());
 543            $item->icon = $OUTPUT->pix_url('f/' . $icon)->out();
 544            $item->sortorder = $file->get_sortorder();
 545
 546            if ($icon == 'zip') {
 547                $item->type = 'zip';
 548            } else {
 549                $item->type = 'file';
 550            }
 551
 552            if ($file->is_directory()) {
 553                $item->filesize = 0;
 554                $item->icon = $OUTPUT->pix_url('f/folder')->out();
 555                $item->type = 'folder';
 556                $foldername = explode('/', trim($item->filepath, '/'));
 557                $item->fullname = trim(array_pop($foldername), '/');
 558            } else {
 559                // do NOT use file browser here!
 560                $item->url = moodle_url::make_draftfile_url($draftitemid, $item->filepath, $item->filename)->out();
 561            }
 562            $list[] = $item;
 563        }
 564    }
 565    $data->itemid = $draftitemid;
 566    $data->list = $list;
 567    return $data;
 568}
 569
 570/**
 571 * Returns draft area itemid for a given element.
 572 *
 573 * @param string $elname name of formlib editor element, or a hidden form field that stores the draft area item id, etc.
 574 * @return integer the itemid, or 0 if there is not one yet.
 575 */
 576function file_get_submitted_draft_itemid($elname) {
 577    $param = optional_param($elname, 0, PARAM_INT);
 578    if ($param) {
 579        require_sesskey();
 580    }
 581    if (is_array($param)) {
 582        if (!empty($param['itemid'])) {
 583            $param = $param['itemid'];
 584        } else {
 585            debugging('Missing itemid, maybe caused by unset maxfiles option', DEBUG_DEVELOPER);
 586            return false;
 587        }
 588    }
 589    return $param;
 590}
 591
 592/**
 593 * Saves files from a draft file area to a real one (merging the list of files).
 594 * Can rewrite URLs in some content at the same time if desired.
 595 *
 596 * @global object
 597 * @global object
 598 * @param integer $draftitemid the id of the draft area to use. Normally obtained
 599 *      from file_get_submitted_draft_itemid('elementname') or similar.
 600 * @param integer $contextid This parameter and the next two identify the file area to save to.
 601 * @param string $component
 602 * @param string $filearea indentifies the file area.
 603 * @param integer $itemid helps identifies the file area.
 604 * @param array $options area options (subdirs=>false, maxfiles=-1, maxbytes=0)
 605 * @param string $text some html content that needs to have embedded links rewritten
 606 *      to the @@PLUGINFILE@@ form for saving in the database.
 607 * @param boolean $forcehttps force https urls.
 608 * @return string if $text was passed in, the rewritten $text is returned. Otherwise NULL.
 609 */
 610function file_save_draft_area_files($draftitemid, $contextid, $component, $filearea, $itemid, array $options=null, $text=null, $forcehttps=false) {
 611    global $USER;
 612
 613    $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
 614    $fs = get_file_storage();
 615
 616    $options = (array)$options;
 617    if (!isset($options['subdirs'])) {
 618        $options['subdirs'] = false;
 619    }
 620    if (!isset($options['maxfiles'])) {
 621        $options['maxfiles'] = -1; // unlimited
 622    }
 623    if (!isset($options['maxbytes'])) {
 624        $options['maxbytes'] = 0; // unlimited
 625    }
 626
 627    $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id');
 628    $oldfiles   = $fs->get_area_files($contextid, $component, $filearea, $itemid, 'id');
 629
 630    if (count($draftfiles) < 2) {
 631        // means there are no files - one file means root dir only ;-)
 632        $fs->delete_area_files($contextid, $component, $filearea, $itemid);
 633
 634    } else if (count($oldfiles) < 2) {
 635        $filecount = 0;
 636        // there were no files before - one file means root dir only ;-)
 637        $file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid);
 638        foreach ($draftfiles as $file) {
 639            if (!$options['subdirs']) {
 640                if ($file->get_filepath() !== '/' or $file->is_directory()) {
 641                    continue;
 642                }
 643            }
 644            if ($options['maxbytes'] and $options['maxbytes'] < $file->get_filesize()) {
 645                // oversized file - should not get here at all
 646                continue;
 647            }
 648            if ($options['maxfiles'] != -1 and $options['maxfiles'] <= $filecount) {
 649                // more files - should not get here at all
 650                break;
 651            }
 652            if (!$file->is_directory()) {
 653                $filecount++;
 654            }
 655            $fs->create_file_from_storedfile($file_record, $file);
 656        }
 657
 658    } else {
 659        // we have to merge old and new files - we want to keep file ids for files that were not changed
 660        // we change time modified for all new and changed files, we keep time created as is
 661        $file_record = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'timemodified'=>time());
 662
 663        $newhashes = array();
 664        foreach ($draftfiles as $file) {
 665            $newhash = $fs->get_pathname_hash($contextid, $component, $filearea, $itemid, $file->get_filepath(), $file->get_filename());
 666            $newhashes[$newhash] = $file;
 667        }
 668        $filecount = 0;
 669        foreach ($oldfiles as $oldfile) {
 670            $oldhash = $oldfile->get_pathnamehash();
 671            if (!isset($newhashes[$oldhash])) {
 672                // delete files not needed any more - deleted by user
 673                $oldfile->delete();
 674                continue;
 675            }
 676            $newfile = $newhashes[$oldhash];
 677            if ($oldfile->get_contenthash() != $newfile->get_contenthash() or $oldfile->get_sortorder() != $newfile->get_sortorder()
 678                or $oldfile->get_status() != $newfile->get_status() or $oldfile->get_license() != $newfile->get_license()
 679                or $oldfile->get_author() != $newfile->get_author() or $oldfile->get_source() != $newfile->get_source()) {
 680                // file was changed, use updated with new timemodified data
 681                $oldfile->delete();
 682                continue;
 683            }
 684            // unchanged file or directory - we keep it as is
 685            unset($newhashes[$oldhash]);
 686            if (!$oldfile->is_directory()) {
 687                $filecount++;
 688            }
 689        }
 690
 691        // now add new/changed files
 692        // the size and subdirectory tests are extra safety only, the UI should prevent it
 693        foreach ($newhashes as $file) {
 694            if (!$options['subdirs']) {
 695                if ($file->get_filepath() !== '/' or $file->is_directory()) {
 696                    continue;
 697                }
 698            }
 699            if ($options['maxbytes'] and $options['maxbytes'] < $file->get_filesize()) {
 700                // oversized file - should not get here at all
 701                continue;
 702            }
 703            if ($options['maxfiles'] != -1 and $options['maxfiles'] <= $filecount) {
 704                // more files - should not get here at all
 705                break;
 706            }
 707            if (!$file->is_directory()) {
 708                $filecount++;
 709            }
 710            $fs->create_file_from_storedfile($file_record, $file);
 711        }
 712    }
 713
 714    // note: do not purge the draft area - we clean up areas later in cron,
 715    //       the reason is that user might press submit twice and they would loose the files,
 716    //       also sometimes we might want to use hacks that save files into two different areas
 717
 718    if (is_null($text)) {
 719        return null;
 720    } else {
 721        return file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps);
 722    }
 723}
 724
 725/**
 726 * Convert the draft file area URLs in some content to @@PLUGINFILE@@ tokens
 727 * ready to be saved in the database. Normally, this is done automatically by
 728 * {@link file_save_draft_area_files()}.
 729 * @param string $text the content to process.
 730 * @param int $draftitemid the draft file area the content was using.
 731 * @param bool $forcehttps whether the content contains https URLs. Default false.
 732 * @return string the processed content.
 733 */
 734function file_rewrite_urls_to_pluginfile($text, $draftitemid, $forcehttps = false) {
 735    global $CFG, $USER;
 736
 737    $usercontext = get_context_instance(CONTEXT_USER, $USER->id);
 738
 739    $wwwroot = $CFG->wwwroot;
 740    if ($forcehttps) {
 741        $wwwroot = str_replace('http://', 'https://', $wwwroot);
 742    }
 743
 744    // relink embedded files if text submitted - no absolute links allowed in database!
 745    $text = str_ireplace("$wwwroot/draftfile.php/$usercontext->id/user/draft/$draftitemid/", '@@PLUGINFILE@@/', $text);
 746
 747    if (strpos($text, 'draftfile.php?file=') !== false) {
 748        $matches = array();
 749        preg_match_all("!$wwwroot/draftfile.php\?file=%2F{$usercontext->id}%2Fuser%2Fdraft%2F{$draftitemid}%2F[^'\",&<>|`\s:\\\\]+!iu", $text, $matches);
 750        if ($matches) {
 751            foreach ($matches[0] as $match) {
 752                $replace = str_ireplace('%2F', '/', $match);
 753                $text = str_replace($match, $replace, $text);
 754            }
 755        }
 756        $text = str_ireplace("$wwwroot/draftfile.php?file=/$usercontext->id/user/draft/$draftitemid/", '@@PLUGINFILE@@/', $text);
 757    }
 758
 759    return $text;
 760}
 761
 762/**
 763 * Set file sort order
 764 * @global object $DB
 765 * @param integer $contextid the context id
 766 * @param string $component
 767 * @param string $filearea file area.
 768 * @param integer $itemid itemid.
 769 * @param string $filepath file path.
 770 * @param string $filename file name.
 771 * @param integer $sortorer the sort order of file.
 772 * @return boolean
 773 */
 774function file_set_sortorder($contextid, $component, $filearea, $itemid, $filepath, $filename, $sortorder) {
 775    global $DB;
 776    $conditions = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea, 'itemid'=>$itemid, 'filepath'=>$filepath, 'filename'=>$filename);
 777    if ($file_record = $DB->get_record('files', $conditions)) {
 778        $sortorder = (int)$sortorder;
 779        $file_record->sortorder = $sortorder;
 780        $DB->update_record('files', $file_record);
 781        return true;
 782    }
 783    return false;
 784}
 785
 786/**
 787 * reset file sort order number to 0
 788 * @global object $DB
 789 * @param integer $contextid the context id
 790 * @param string $component
 791 * @param string $filearea file area.
 792 * @param integer $itemid itemid.
 793 * @return boolean
 794 */
 795function file_reset_sortorder($contextid, $component, $filearea, $itemid=false) {
 796    global $DB;
 797
 798    $conditions = array('contextid'=>$contextid, 'component'=>$component, 'filearea'=>$filearea);
 799    if ($itemid !== false) {
 800        $conditions['itemid'] = $itemid;
 801    }
 802
 803    $file_records = $DB->get_records('files', $conditions);
 804    foreach ($file_records as $file_record) {
 805        $file_record->sortorder = 0;
 806        $DB->update_record('files', $file_record);
 807    }
 808    return true;
 809}
 810
 811/**
 812 * Returns description of upload error
 813 *
 814 * @param int $errorcode found in $_FILES['filename.ext']['error']
 815 * @return string error description string, '' if ok
 816 */
 817function file_get_upload_error($errorcode) {
 818
 819    switch ($errorcode) {
 820    case 0: // UPLOAD_ERR_OK - no error
 821        $errmessage = '';
 822        break;
 823
 824    case 1: // UPLOAD_ERR_INI_SIZE
 825        $errmessage = get_string('uploadserverlimit');
 826        break;
 827
 828    case 2: // UPLOAD_ERR_FORM_SIZE
 829        $errmessage = get_string('uploadformlimit');
 830        break;
 831
 832    case 3: // UPLOAD_ERR_PARTIAL
 833        $errmessage = get_string('uploadpartialfile');
 834        break;
 835
 836    case 4: // UPLOAD_ERR_NO_FILE
 837        $errmessage = get_string('uploadnofilefound');
 838        break;
 839
 840    // Note: there is no error with a value of 5
 841
 842    case 6: // UPLOAD_ERR_NO_TMP_DIR
 843        $errmessage = get_string('uploadnotempdir');
 844        break;
 845
 846    case 7: // UPLOAD_ERR_CANT_WRITE
 847        $errmessage = get_string('uploadcantwrite');
 848        break;
 849
 850    case 8: // UPLOAD_ERR_EXTENSION
 851        $errmessage = get_string('uploadextension');
 852        break;
 853
 854    default:
 855        $errmessage = get_string('uploadproblem');
 856    }
 857
 858    return $errmessage;
 859}
 860
 861/**
 862 * Recursive function formating an array in POST parameter
 863 * @param array $arraydata - the array that we are going to format and add into &$data array
 864 * @param string $currentdata - a row of the final postdata array at instant T
 865 *                when finish, it's assign to $data under this format: name[keyname][][]...[]='value'
 866 * @param array $data - the final data array containing all POST parameters : 1 row = 1 parameter
 867 */
 868function format_array_postdata_for_curlcall($arraydata, $currentdata, &$data) {
 869        foreach ($arraydata as $k=>$v) {
 870            $newcurrentdata = $currentdata;
 871            if (is_array($v)) { //the value is an array, call the function recursively
 872                $newcurrentdata = $newcurrentdata.'['.urlencode($k).']';
 873                format_array_postdata_for_curlcall($v, $newcurrentdata, $data);
 874            }  else { //add the POST parameter to the $data array
 875                $data[] = $newcurrentdata.'['.urlencode($k).']='.urlencode($v);
 876            }
 877        }
 878}
 879
 880/**
 881 * Transform a PHP array into POST parameter
 882 * (see the recursive function format_array_postdata_for_curlcall)
 883 * @param array $postdata
 884 * @return array containing all POST parameters  (1 row = 1 POST parameter)
 885 */
 886function format_postdata_for_curlcall($postdata) {
 887        $data = array();
 888        foreach ($postdata as $k=>$v) {
 889            if (is_array($v)) {
 890                $currentdata = urlencode($k);
 891                format_array_postdata_for_curlcall($v, $currentdata, $data);
 892            }  else {
 893                $data[] = urlencode($k).'='.urlencode($v);
 894            }
 895        }
 896        $convertedpostdata = implode('&', $data);
 897        return $convertedpostdata;
 898}
 899
 900
 901
 902
 903/**
 904 * Fetches content of file from Internet (using proxy if defined). Uses cURL extension if present.
 905 * Due to security concerns only downloads from http(s) sources are supported.
 906 *
 907 * @global object
 908 * @param string $url file url starting with http(s)://
 909 * @param array $headers http headers, null if none. If set, should be an
 910 *   associative array of header name => value pairs.
 911 * @param array $postdata array means use POST request with given parameters
 912 * @param bool $fullresponse return headers, responses, etc in a similar way snoopy does
 913 *   (if false, just returns content)
 914 * @param int $timeout timeout for complete download process including all file transfer
 915 *   (default 5 minutes)
 916 * @param int $connecttimeout timeout for connection to server; this is the timeout that
 917 *   usually happens if the remote server is completely down (default 20 seconds);
 918 *   may not work when using proxy
 919 * @param bool $skipcertverify If true, the peer's SSL certificate will not be checked. Only use this when already in a trusted location.
 920 * @param string $tofile store the downloaded content to file instead of returning it
 921 * @return mixed false if request failed or content of the file as string if ok. true if file downloaded into $tofile successfully.
 922 */
 923function download_file_content($url, $headers=null, $postdata=null, $fullresponse=false, $timeout=300, $connecttimeout=20, $skipcertverify=false, $tofile=NULL) {
 924    global $CFG;
 925
 926    // some extra security
 927    $newlines = array("\r", "\n");
 928    if (is_array($headers) ) {
 929        foreach ($headers as $key => $value) {
 930            $headers[$key] = str_replace($newlines, '', $value);
 931        }
 932    }
 933    $url = str_replace($newlines, '', $url);
 934    if (!preg_match('|^https?://|i', $url)) {
 935        if ($fullresponse) {
 936            $response = new stdClass();
 937            $response->status        = 0;
 938            $response->headers       = array();
 939            $response->response_code = 'Invalid protocol specified in url';
 940            $response->results       = '';
 941            $response->error         = 'Invalid protocol specified in url';
 942            return $response;
 943        } else {
 944            return false;
 945        }
 946    }
 947
 948    // check if proxy (if used) should be bypassed for this url
 949    $proxybypass = is_proxybypass($url);
 950
 951    if (!$ch = curl_init($url)) {
 952        debugging('Can not init curl.');
 953        return false;
 954    }
 955
 956    // set extra headers
 957    if (is_array($headers) ) {
 958        $headers2 = array();
 959        foreach ($headers as $key => $value) {
 960            $headers2[] = "$key: $value";
 961        }
 962        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers2);
 963    }
 964
 965
 966    if ($skipcertverify) {
 967        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
 968    }
 969
 970    // use POST if requested
 971    if (is_array($postdata)) {
 972        $postdata = format_postdata_for_curlcall($postdata);
 973        curl_setopt($ch, CURLOPT_POST, true);
 974        curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
 975    }
 976
 977    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 978    curl_setopt($ch, CURLOPT_HEADER, false);
 979    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connecttimeout);
 980    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
 981    if (!ini_get('open_basedir') and !ini_get('safe_mode')) {
 982        // TODO: add version test for '7.10.5'
 983        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
 984        curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
 985    }
 986
 987    if (!empty($CFG->proxyhost) and !$proxybypass) {
 988        // SOCKS supported in PHP5 only
 989        if (!empty($CFG->proxytype) and ($CFG->proxytype == 'SOCKS5')) {
 990            if (defined('CURLPROXY_SOCKS5')) {
 991                curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
 992            } else {
 993                curl_close($ch);
 994                if ($fullresponse) {
 995                    $response = new stdClass();
 996                    $response->status        = '0';
 997                    $response->headers       = array();
 998                    $response->response_code = 'SOCKS5 proxy is not supported in PHP4';
 999                    $response->results       = '';
1000                    $response->error         = 'SOCKS5 proxy is not supported in PHP4';
1001                    return $response;
1002                } else {
1003                    debugging("SOCKS5 proxy is not supported in PHP4.", DEBUG_ALL);
1004                    return false;
1005                }
1006            }
1007        }
1008
1009        curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, false);
1010
1011        if (empty($CFG->proxyport)) {
1012            curl_setopt($ch, CURLOPT_PROXY, $CFG->proxyhost);
1013        } else {
1014            curl_setopt($ch, CURLOPT_PROXY, $CFG->proxyhost.':'.$CFG->proxyport);
1015        }
1016
1017        if (!empty($CFG->proxyuser) and !empty($CFG->proxypassword)) {
1018            curl_setopt($ch, CURLOPT_PROXYUSERPWD, $CFG->proxyuser.':'.$CFG->proxypassword);
1019            if (defined('CURLOPT_PROXYAUTH')) {
1020                // any proxy authentication if PHP 5.1
1021                curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_NTLM);
1022            }
1023        }
1024    }
1025
1026    // set up header and content handlers
1027    $received = new stdClass();
1028    $received->headers = array(); // received headers array
1029    $received->tofile  = $tofile;
1030    $received->fh      = null;
1031    curl_setopt($ch, CURLOPT_HEADERFUNCTION, partial('download_file_content_header_handler', $received));
1032    if ($tofile) {
1033        curl_setopt($ch, CURLOPT_WRITEFUNCTION, partial('download_file_content_write_handler', $received));
1034    }
1035
1036    $result = curl_exec($ch);
1037
1038    // try to detect encoding problems
1039    if ((curl_errno($ch) == 23 or curl_errno($ch) == 61) and defined('CURLOPT_ENCODING')) {
1040        curl_setopt($ch, CURLOPT_ENCODING, 'none');
1041        $result = curl_exec($ch);
1042    }
1043
1044    if ($received->fh) {
1045        fclose($received->fh);
1046    }
1047
1048    if (curl_errno($ch)) {
1049        $error    = curl_error($ch);
1050        $error_no = curl_errno($ch);
1051        curl_close($ch);
1052
1053        if ($fullresponse) {
1054            $response = new stdClass();
1055            if ($error_no == 28) {
1056                $response->status    = '-100'; // mimic snoopy
1057            } else {
1058                $response->status    = '0';
1059            }
1060            $response->headers       = array();
1061            $response->response_code = $error;
1062            $response->results       = false;
1063            $response->error         = $error;
1064            return $response;
1065        } else {
1066            debugging("cURL request for \"$url\" failed with: $error ($error_no)", DEBUG_ALL);
1067            return false;
1068        }
1069
1070    } else {
1071        $info = curl_getinfo($ch);
1072        curl_close($ch);
1073
1074        if (empty($info['http_code'])) {
1075            // for security reasons we support only true http connections (Location: file:// exploit prevention)
1076            $response = new stdClass();
1077            $response->status        = '0';
1078            $response->headers       = array();
1079            $response->response_code = 'Unknown cURL error';
1080            $response->results       = false; // do NOT change this, we really want to ignore the result!
1081            $response->error         = 'Unknown cURL error';
1082
1083        } else {
1084            $response = new stdClass();;
1085            $response->status        = (string)$info['http_code'];
1086            $response->headers       = $received->headers;
1087            $response->response_code = $received->headers[0];
1088            $response->results       = $result;
1089            $response->error         = '';
1090        }
1091
1092        if ($fullresponse) {
1093            return $response;
1094        } else if ($info['http_code'] != 200) {
1095            debugging("cURL request for \"$url\" failed, HTTP response code: ".$response->response_code, DEBUG_ALL);
1096            return false;
1097        } else {
1098            return $response->results;
1099        }
1100    }
1101}
1102
1103/**
1104 * internal implementation
1105 */
1106function download_file_content_header_handler($received, $ch, $header) {
1107    $received->headers[] = $header;
1108    return strlen($header);
1109}
1110
1111/**
1112 * internal implementation
1113 */
1114function download_file_content_write_handler($received, $ch, $data) {
1115    if (!$received->fh) {
1116        $received->fh = fopen($received->tofile, 'w');
1117        if ($received->fh === false) {
1118            // bad luck, file creation or overriding failed
1119            return 0;
1120        }
1121    }
1122    if (fwrite($received->fh, $data) === false) {
1123        // bad luck, write failed, let's abort completely
1124        return 0;
1125    }
1126    return strlen($data);
1127}
1128
1129/**
1130 * @return array List of information about file types based on extensions.
1131 *   Associative array of extension (lower-case) to associative array
1132 *   from 'element name' to data. Current element names are 'type' and 'icon'.
1133 *   Unknown types should use the 'xxx' entry which includes defaults.
1134 */
1135function get_mimetypes_array() {
1136    static $mimearray = array (
1137        'xxx'  => array ('type'=>'document/unknown', 'icon'=>'unknown'),
1138        '3gp'  => array ('type'=>'video/quicktime', 'icon'=>'video'),
1139        'aac'  => array ('type'=>'audio/aac', 'icon'=>'audio'),
1140        'ai'   => array ('type'=>'application/postscript', 'icon'=>'image'),
1141        'aif'  => array ('type'=>'audio/x-aiff', 'icon'=>'audio'),
1142        'aiff' => array ('type'=>'audio/x-aiff', 'icon'=>'audio'),
1143        'aifc' => array ('type'=>'audio/x-aiff', 'icon'=>'audio'),
1144        'applescript'  => array ('type'=>'text/plain', 'icon'=>'text'),
1145        'asc'  => array ('type'=>'text/plain', 'icon'=>'text'),
1146        'asm'  => array ('type'=>'text/plain', 'icon'=>'text'),
1147        'au'   => array ('type'=>'audio/au', 'icon'=>'audio'),
1148        'avi'  => array ('type'=>'video/x-ms-wm', 'icon'=>'avi'),
1149        'bmp'  => array ('type'=>'image/bmp', 'icon'=>'image'),
1150        'c'    => array ('type'=>'text/plain', 'icon'=>'text'),
1151        'cct'  => array ('type'=>'shockwave/director', 'icon'=>'flash'),
1152        'cpp'  => array ('type'=>'text/plain', 'icon'=>'text'),
1153        'cs'   => array ('type'=>'application/x-csh', 'icon'=>'text'),
1154        'css'  => array ('type'=>'text/css', 'icon'=>'text'),
1155        'csv'  => array ('type'=>'text/csv', 'icon'=>'excel'),
1156        'dv'   => array ('type'=>'video/x-dv', 'icon'=>'video'),
1157        'dmg'  => array ('type'=>'application/octet-stream', 'icon'=>'dmg'),
1158
1159        'doc'  => array ('type'=>'application/msword', 'icon'=>'word'),
1160        'docx' => array ('type'=>'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'icon'=>'docx'),
1161        'docm' => array ('type'=>'application/vnd.ms-word.document.macroEnabled.12', 'icon'=>'docm'),
1162        'dotx' => array ('type'=>'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'icon'=>'dotx'),
1163        'dotm' => array ('type'=>'application/vnd.ms-word.template.macroEnabled.12', 'icon'=>'dotm'),
1164
1165        'dcr'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
1166        'dif'  => array ('type'=>'video/x-dv', 'icon'=>'video'),
1167        'dir'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
1168        'dxr'  => array ('type'=>'application/x-director', 'icon'=>'flash'),
1169        'eps'  => array ('type'=>'application/postscript', 'icon'=>'pdf'),
1170        'fdf'  => array ('type'=>'application/pdf', 'icon'=>'pdf'),
1171        'flv'  => array ('type'=>'video/x-flv', 'icon'=>'video'),
1172        'f4v'  => array ('type'=>'video/mp4', 'icon'=>'video'),
1173        'gif'  => array ('type'=>'image/gif', 'icon'=>'image'),
1174        'gtar' => array ('type'=>'application/x-gtar', 'icon'=>'zip'),
1175        'tgz'  => array ('type'=>'application/g-zip', 'icon'=>'zip'),
1176        'gz'   => array ('type'=>'application/g-zip', 'icon'=>'zip'),
1177        'gzip' => array ('type'=>'application/g-zip', 'icon'=>'zip'),
1178        'h'    => array ('type'=>'text/plain', 'icon'=>'text'),
1179        'hpp'  => array ('type'=>'text/plain', 'icon'=>'text'),
1180        'hqx'  => array ('type'=>'application/mac-binhex40', 'icon'=>'zip'),
1181        'htc'  => array ('type'=>'text/x-component', 'icon'=>'text'),
1182        'html' => array ('type'=>'text/html', 'icon'=>'html'),
1183        'xhtml'=> array ('type'=>'application/xhtml+xml', 'icon'=>'html'),
1184        'htm'  => array ('type'=>'text/html', 'icon'=>'html'),
1185        'ico'  => array ('type'=>'image/vnd.microsoft.icon', 'icon'=>'image'),
1186        'ics'  => array ('type'=>'text/calendar', 'icon'=>'text'),
1187        'isf'  => array ('type'=>'application/inspiration', 'icon'=>'isf'),
1188        'ist'  => array ('type'=>'application/inspiration.template', 'icon'=>'isf'),
1189        'java' => array ('type'=>'text/plain', 'icon'=>'text'),
1190        'jcb'  => array ('type'=>'text/xml', 'icon'=>'jcb'),
1191        'jcl'  => array ('type'=>'text/xml', 'icon'=>'jcl'),
1192        'jcw'  => array ('type'=>'text/xml', 'icon'=>'jcw'),
1193        'jmt'  => array ('type'=>'text/xml', 'icon'=>'jmt'),
1194        'jmx'  => array ('type'=>'text/xml', 'icon'=>'jmx'),
1195        'jpe'  => array ('type'=>'image/jpeg', 'icon'=>'image'),
1196        'jpeg' => array ('type'=>'image/jpeg', 'icon'=>'image'),
1197        'jpg'  => array ('type'=>'image/jpeg', 'icon'=>'image'),
1198        'jqz'  => array ('type'=>'text/xml', 'icon'=>'jqz'),
1199        'js'   => array ('type'=>'application/x-javascript', 'icon'=>'text'),
1200        'latex'=> array ('type'=>'application/x-latex', 'icon'=>'text'),
1201        'm'    => array ('type'=>'text/plain', 'icon'=>'text'),
1202        'mbz'  => array ('type'=>'application/vnd.moodle.backup', 'icon'=>'moodle'),
1203        'mov'  => array ('type'=>'video/quicktime', 'icon'=>'video'),
1204        'movie'=> array ('type'=>'video/x-sgi-movie', 'icon'=>'video'),
1205        'm3u'  => array ('type'=>'audio/x-mpegurl', 'icon'=>'audio'),
1206        'mp3'  => array ('type'=>'audio/mp3', 'icon'=>'audio'),
1207        'mp4'  => array ('type'=>'video/mp4', 'icon'=>'video'),
1208        'm4v'  => array ('type'=>'video/mp4', 'icon'=>'video'),
1209        'm4a'  => array ('type'=>'audio/mp4', 'icon'=>'audio'),
1210        'mpeg' => array ('type'=>'video/mpeg', 'icon'=>'video'),
1211        'mpe'  => array ('type'=>'video/mpeg', 'icon'=>'video'),
1212        'mpg'  => array ('type'=>'video/mpeg', 'icon'=>'video'),
1213
1214        'odt'  => array ('type'=>'application/vnd.oasis.opendocument.text', 'icon'=>'odt'),
1215        'ott'  => array ('type'=>'application/vnd.oasis.opendocument.text-template', 'icon'=>'odt'),
1216        'oth'  => array ('type'=>'application/vnd.oasis.opendocument.text-web', 'icon'=>'odt'),
1217        'odm'  => array ('type'=>'application/vnd.oasis.opendocument.text-master', 'icon'=>'odm'),
1218        'odg'  => array ('type'=>'application/vnd.oasis.opendocument.graphics', 'icon'=>'odg'),
1219        'otg'  => array ('type'=>'application/vnd.oasis.opendocument.graphics-template', 'icon'=>'odg'),
1220        'odp'  => array ('type'=>'application/vnd.oasis.opendocument.presentation', 'icon'=>'odp'),
1221        'otp'  => array ('type'=>'application/vnd.oasis.opendocument.presentation-template', 'icon'=>'odp'),
1222        'ods'  => array ('type'=>'application/vnd.oasis.opendocument.spreadsheet', 'icon'=>'ods'),
1223        'ots'  => array ('type'=>'application/vnd.oasis.opendocument.spreadsheet-template', 'icon'=>'ods'),
1224        'odc'  => array ('type'=>'application/vnd.oasis.opendocument.chart', 'icon'=>'odc'),
1225        'odf'  => array ('type'=>'application/vnd.oasis.opendocument.formula', 'icon'=>'odf'),
1226        'odb'  => array ('type'=>'application/vnd.oasis.opendocument.database', 'icon'=>'odb'),
1227        'odi'  => array ('type'=>'application/vnd.oasis.opendocument.image', 'icon'=>'odi'),
1228        'oga'  => array ('type'=>'audio/ogg', 'icon'=>'audio'),
1229        'ogg'  => array ('type'=>'audio/ogg', 'icon'=>'audio'),
1230        'ogv'  => array ('type'=>'video/ogg', 'icon'=>'video'),
1231
1232        'pct'  => array ('type'=>'image/pict', 'icon'=>'image'),
1233        'pdf'  => array ('type'=>'application/pdf', 'icon'=>'pdf'),
1234        'php'  => array ('type'=>'text/plain', 'icon'=>'text'),
1235        'pic'  => array ('type'=>'image/pict', 'icon'=>'image'),
1236        'pict' => array ('type'=>'image/pict', 'icon'=>'image'),
1237        'png'  => array ('type'=>'image/png', 'icon'=>'image'),
1238
1239        'pps'  => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'powerpoint'),
1240        'ppt'  => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'powerpoint'),
1241        'pptx' => array ('type'=>'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'icon'=>'pptx'),
1242        'pptm' => array ('type'=>'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'icon'=>'pptm'),
1243        'potx' => array ('type'=>'application/vnd.openxmlformats-officedocument.presentationml.template', 'icon'=>'potx'),
1244        'potm' => array ('type'=>'application/vnd.ms-powerpoint.template.macroEnabled.12', 'icon'=>'potm'),
1245

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