diff options
Diffstat (limited to 'application')
-rw-r--r-- | application/controllers/file.php | 186 | ||||
-rw-r--r-- | application/helpers/filebin_helper.php | 22 | ||||
-rw-r--r-- | application/models/file_mod.php | 214 |
3 files changed, 210 insertions, 212 deletions
diff --git a/application/controllers/file.php b/application/controllers/file.php index b8306b5bc..c84620d4f 100644 --- a/application/controllers/file.php +++ b/application/controllers/file.php @@ -62,14 +62,190 @@ class File extends CI_Controller { if(isset($_FILES['file'])) { $this->do_upload(); } elseif ($id != "file" && $this->file_mod->id_exists($id)) { - $this->file_mod->download(); + $this->_download(); } elseif ($id && $id != "file") { - $this->file_mod->non_existent(); + $this->_non_existent(); } else { $this->upload_form(); } } + function _download() + { + $id = $this->uri->segment(1); + $mode = $this->uri->segment(2); + + $filedata = $this->file_mod->get_filedata($id); + $file = $this->file_mod->file($filedata['hash']); + + if (!$this->file_mod->valid_id($id)) { + $this->non_existent(); + return; + } + + // don't allow unowned files to be downloaded + if ($filedata["user"] == 0) { + $this->file_mod->non_existent(); + return; + } + + // helps to keep traffic low when reloading + $etag = $filedata["hash"]."-".$filedata["date"]; + + // autodetect the mode for highlighting if the URL contains a / after the ID (/ID/) + // /ID/mode disables autodetection + $autodetect_mode = !$mode && substr_count(ltrim($this->uri->uri_string(), "/"), '/') >= 1; + + if ($autodetect_mode) { + $mode = $this->file_mod->get_highlight_mode($filedata["mimetype"], $filedata["filename"]); + } + + // resolve aliases of modes + // this is mainly used for compatibility + $mode = $this->file_mod->resolve_mode_alias($mode); + + // create the qr code for /ID/ + if ($mode == "qr") { + handle_etag($etag); + header("Content-disposition: inline; filename=\"".$id."_qr.png\"\n"); + header("Content-Type: image/png\n"); + passthru('qrencode -s 10 -o - '.escapeshellarg(site_url($id).'/')); + exit(); + } + + // user wants to the the plain file + if ($mode == 'plain') { + handle_etag($etag); + rangeDownload($file, $filedata["filename"], "text/plain"); + exit(); + } + + if ($mode == 'info') { + $this->_display_info($id); + return; + } + + // if there is no mimetype mapping we can't highlight it + $can_highlight = $this->file_mod->can_highlight($filedata["mimetype"]); + + $filesize_too_big = filesize($file) > $this->config->item('upload_max_text_size'); + + if (!$can_highlight || $filesize_too_big || !$mode) { + // prevent javascript from being executed and forbid frames + // this should allow us to serve user submitted HTML content without huge security risks + foreach (array("X-WebKit-CSP", "X-Content-Security-Policy") as $header_name) { + header("$header_name: allow 'none'; img-src *; media-src *; font-src *; style-src * 'unsafe-inline'; script-src 'none'; object-src *; frame-src 'none'; "); + } + handle_etag($etag); + rangeDownload($file, $filedata["filename"], $filedata["mimetype"]); + exit(); + } + + $this->data['title'] = htmlspecialchars($filedata['filename']); + $this->data['id'] = $id; + + header("Content-Type: text/html\n"); + + $this->data['current_highlight'] = htmlspecialchars($mode); + $this->data['timeout'] = $this->file_mod->get_timeout_string($id); + + echo $this->load->view($this->var->view_dir.'/html_header', $this->data, true); + + // highlight the file and chache the result + $this->load->library("MemcacheLibrary"); + if (! $cached = $this->memcachelibrary->get($filedata['hash'].'_'.$mode)) { + ob_start(); + if ($mode == "rmd") { + echo '<td class="markdownrender">'."\n"; + passthru('perl '.FCPATH.'scripts/Markdown.pl '.escapeshellarg($file)); + } elseif ($mode == "ascii") { + echo '<td class="code"><pre class="text">'."\n"; + passthru('perl '.FCPATH.'scripts/ansi2html '.escapeshellarg($file)); + echo "</pre>\n"; + } else { + echo '<td class="numbers"><pre>'; + // generate line numbers (links) + passthru('perl -ne \'print "<a href=\"#n$.\" class=\"no\" id=\"n$.\">$.</a>\n"\' '.escapeshellarg($file)); + echo '</pre></td><td class="code">'."\n"; + $this->load->library('geshi'); + $this->geshi->initialize(array('set_language' => $mode, 'set_source' => file_get_contents($file), 'enable_classes' => 'true')); + echo $this->geshi->parse_code(); + } + $cached = ob_get_contents(); + ob_end_clean(); + $this->memcachelibrary->set($filedata['hash'].'_'.$mode, $cached, 100); + } + echo $cached; + + echo $this->load->view($this->var->view_dir.'/html_footer', $this->data, true); + + return; + } + + function _display_info($id) + { + $this->data["title"] .= " - Info $id"; + $this->data["filedata"] = $this->file_mod->get_filedata($id); + $this->data["id"] = $id; + $this->data['timeout'] = $this->file_mod->get_timeout_string($id); + + $this->load->view($this->var->view_dir.'/header', $this->data); + $this->load->view($this->var->view_dir.'/file_info', $this->data); + $this->load->view($this->var->view_dir.'/footer', $this->data); + } + + function _non_existent() + { + $this->data["title"] .= " - Not Found"; + $this->output->set_status_header(404); + $this->load->view($this->var->view_dir.'/header', $this->data); + $this->load->view($this->var->view_dir.'/non_existent', $this->data); + $this->load->view($this->var->view_dir.'/footer', $this->data); + } + + function _show_url($id, $mode) + { + $redirect = false; + + if (!$this->muser->logged_in()) { + // keep the upload but require the user to login + $this->session->set_userdata("last_upload", array( + "id" => $id, + "mode" => $mode + )); + $this->session->set_flashdata("uri", "file/claim_id"); + $this->muser->require_access(); + } + + if ($mode) { + $this->data['url'] = site_url($id).'/'.$mode; + } else { + $this->data['url'] = site_url($id).'/'; + + $filedata = $this->file_mod->get_filedata($id); + $file = $this->file_mod->file($filedata['hash']); + $type = $filedata['mimetype']; + $mode = $this->file_mod->should_highlight($type); + + // If we detected a highlightable file redirect, + // otherwise show the URL because browsers would just show a DL dialog + if ($mode) { + $redirect = true; + } + } + + if (is_cli_client()) { + $redirect = false; + } + if ($redirect) { + redirect($this->data['url'], "location", 303); + } else { + $this->load->view($this->var->view_dir.'/header', $this->data); + $this->load->view($this->var->view_dir.'/show_url', $this->data); + $this->load->view($this->var->view_dir.'/footer', $this->data); + } + } + function client() { $this->data['title'] .= ' - Client'; @@ -278,7 +454,7 @@ class File extends CI_Controller { file_put_contents($file, $content); chmod($file, 0600); $this->file_mod->add_file($hash, $id, $filename); - $this->file_mod->show_url($id, false); + $this->_show_url($id, false); } // Handles uploaded files @@ -337,7 +513,7 @@ class File extends CI_Controller { move_uploaded_file($_FILES['file']['tmp_name'], $file); chmod($file, 0600); $this->file_mod->add_file($hash, $id, $filename); - $this->file_mod->show_url($id, $extension); + $this->_show_url($id, $extension); } function claim_id() @@ -357,7 +533,7 @@ class File extends CI_Controller { $this->session->unset_userdata("last_upload"); - $this->file_mod->show_url($id, $last_upload["mode"]); + $this->_show_url($id, $last_upload["mode"]); } /* Functions below this comment can only be run via the CLI diff --git a/application/helpers/filebin_helper.php b/application/helpers/filebin_helper.php index d9a490bd6..f6663144e 100644 --- a/application/helpers/filebin_helper.php +++ b/application/helpers/filebin_helper.php @@ -221,4 +221,26 @@ function link_with_mtime($file) return $link; } +function handle_etag($etag) +{ + $etag = strtolower($etag); + $modified = true; + + if(isset($_SERVER['HTTP_IF_NONE_MATCH'])) { + $oldtag = trim(strtolower($_SERVER['HTTP_IF_NONE_MATCH']), '"'); + if($oldtag == $etag) { + $modified = false; + } else { + $modified = true; + } + } + + header('Etag: "'.$etag.'"'); + + if (!$modified) { + header("HTTP/1.1 304 Not Modified"); + exit(); + } +} + # vim: set noet: diff --git a/application/models/file_mod.php b/application/models/file_mod.php index f203f8ef9..a60292138 100644 --- a/application/models/file_mod.php +++ b/application/models/file_mod.php @@ -9,17 +9,13 @@ class File_mod extends CI_Model { - public $data; - function __construct() { parent::__construct(); - $this->data =& get_instance()->data; $this->load->model("muser"); } // Returns an unused ID - // TODO: make threadsafe function new_id() { $id = random_alphanum(3,6); @@ -79,7 +75,6 @@ class File_mod extends CI_Model { } // Add a hash to the DB - // TODO: Should only update not insert; see new_id() function add_file($hash, $id, $filename) { $userid = $this->muser->get_userid(); @@ -103,58 +98,6 @@ class File_mod extends CI_Model { ", array($userid, $id)); } - function show_url($id, $mode) - { - $redirect = false; - - if (!$this->muser->logged_in()) { - // keep the upload but require the user to login - $this->session->set_userdata("last_upload", array( - "id" => $id, - "mode" => $mode - )); - $this->session->set_flashdata("uri", "file/claim_id"); - $this->muser->require_access(); - } - - if ($mode) { - $this->data['url'] = site_url($id).'/'.$mode; - } else { - $this->data['url'] = site_url($id).'/'; - - $filedata = $this->get_filedata($id); - $file = $this->file($filedata['hash']); - $type = $filedata['mimetype']; - $mode = $this->mime2mode($type); - - // If we detected a highlightable file redirect, - // otherwise show the URL because browsers would just show a DL dialog - if ($mode) { - $redirect = true; - } - } - - if (is_cli_client()) { - $redirect = false; - } - if ($redirect) { - redirect($this->data['url'], "location", 303); - } else { - $this->load->view($this->var->view_dir.'/header', $this->data); - $this->load->view($this->var->view_dir.'/show_url', $this->data); - $this->load->view($this->var->view_dir.'/footer', $this->data); - } - } - - function non_existent() - { - $this->data["title"] .= " - Not Found"; - $this->output->set_status_header(404); - $this->load->view($this->var->view_dir.'/header', $this->data); - $this->load->view($this->var->view_dir.'/non_existent', $this->data); - $this->load->view($this->var->view_dir.'/footer', $this->data); - } - // remove old/invalid/broken IDs function valid_id($id) { @@ -194,140 +137,6 @@ class File_mod extends CI_Model { return true; } - private function handle_etag($etag) { - $etag = strtolower($etag); - $modified = true; - - if(isset($_SERVER['HTTP_IF_NONE_MATCH'])) { - $oldtag = trim(strtolower($_SERVER['HTTP_IF_NONE_MATCH']), '"'); - if($oldtag == $etag) { - $modified = false; - } else { - $modified = true; - } - } - - header('Etag: "'.$etag.'"'); - - if (!$modified) { - header("HTTP/1.1 304 Not Modified"); - exit(); - } - } - - // download a given ID - // TODO: make smaller - function download() - { - $id = $this->uri->segment(1); - $mode = $this->uri->segment(2); - - $filedata = $this->get_filedata($id); - $file = $this->file($filedata['hash']); - - if (!$this->valid_id($id)) { - $this->non_existent(); - return; - } - - // don't allow unowned files to be downloaded - if ($filedata["user"] == 0) { - $this->non_existent(); - return; - } - - // helps to keep traffic low when reloading - $etag = $filedata["hash"]."-".$filedata["date"]; - - $type = $filedata['mimetype']; - - // autodetect the mode for highlighting if the URL contains a / after the ID (/ID/) - // /ID/mode disables autodetection - $autodetect_mode = !$mode && substr_count(ltrim($this->uri->uri_string(), "/"), '/') >= 1; - - if ($autodetect_mode) { - $mode = $this->get_highlight_mode($type, $filedata["filename"]); - } - // resolve aliases of modes - // this is mainly used for compatibility - $mode = $this->resolve_mode_alias($mode); - - // create the qr code for /ID/ - if ($mode == "qr") { - $this->handle_etag($etag); - header("Content-disposition: inline; filename=\"".$id."_qr.png\"\n"); - header("Content-Type: image/png\n"); - passthru('qrencode -s 10 -o - '.escapeshellarg(site_url($id).'/')); - exit(); - } - - // user wants to the the plain file - if ($mode == 'plain') { - $this->handle_etag($etag); - rangeDownload($file, $filedata["filename"], "text/plain"); - exit(); - } - - if ($mode == 'info') { - $this->display_info($id); - return; - } - - // if there is no mimetype mapping we can't highlight it - $can_highlight = $this->can_highlight($type); - - $filesize_too_big = filesize($file) > $this->config->item('upload_max_text_size'); - - if (!$can_highlight || $filesize_too_big || !$mode) { - $this->handle_etag($etag); - foreach (array("X-WebKit-CSP", "X-Content-Security-Policy") as $header_name) { - header("$header_name: allow 'none'; img-src *; media-src *; font-src *; style-src * 'unsafe-inline'; script-src 'none'; object-src *; frame-src 'none'; "); - } - rangeDownload($file, $filedata["filename"], $type); - exit(); - } - - $this->data['title'] = htmlspecialchars($filedata['filename']); - $this->data['id'] = $id; - - header("Content-Type: text/html\n"); - - $this->data['current_highlight'] = htmlspecialchars($mode); - $this->data['timeout'] = $this->get_timeout_string($id); - - echo $this->load->view($this->var->view_dir.'/html_header', $this->data, true); - - // highlight the file and chache the result - $this->load->library("MemcacheLibrary"); - if (! $cached = $this->memcachelibrary->get($filedata['hash'].'_'.$mode)) { - ob_start(); - if ($mode == "rmd") { - echo '<td class="markdownrender">'."\n"; - passthru('perl '.FCPATH.'scripts/Markdown.pl '.escapeshellarg($file)); - } elseif ($mode == "ascii") { - echo '<td class="code"><pre class="text">'."\n"; - passthru('perl '.FCPATH.'scripts/ansi2html '.escapeshellarg($file)); - echo "</pre>\n"; - } else { - echo '<td class="numbers"><pre>'; - // generate line numbers (links) - passthru('perl -ne \'print "<a href=\"#n$.\" class=\"no\" id=\"n$.\">$.</a>\n"\' '.escapeshellarg($file)); - echo '</pre></td><td class="code">'."\n"; - $this->load->library('geshi'); - $this->geshi->initialize(array('set_language' => $mode, 'set_source' => file_get_contents($file), 'enable_classes' => 'true')); - echo $this->geshi->parse_code(); - } - $cached = ob_get_contents(); - ob_end_clean(); - $this->memcachelibrary->set($filedata['hash'].'_'.$mode, $cached, 100); - } - echo $cached; - - echo $this->load->view($this->var->view_dir.'/html_footer', $this->data, true); - - exit(); - } - function get_timeout_string($id) { $filedata = $this->get_filedata($id); @@ -356,22 +165,6 @@ class File_mod extends CI_Model { } } - function display_info($id) - { - $this->data["title"] .= " - Info $id"; - $this->data["filedata"] = $this->get_filedata($id); - $this->data["id"] = $id; - $this->data['timeout'] = $this->get_timeout_string($id); - - if (!isset($this->data["can_delete"])) { - $this->data["can_delete"] = false; - } - - $this->load->view($this->var->view_dir.'/header', $this->data); - $this->load->view($this->var->view_dir.'/file_info', $this->data); - $this->load->view($this->var->view_dir.'/footer', $this->data); - } - function delete_id($id) { $this->muser->require_access(); @@ -401,6 +194,13 @@ class File_mod extends CI_Model { return true; } + function should_highlight($type) + { + if ($this->mime2mode($type)) return true; + + return false; + } + // Allow certain types to be highlight without doing it automatically function can_highlight($type) { |