*
* Licensed under AGPLv3
* (see COPYING for full license text)
*
*/
class File extends MY_Controller {
protected $json_enabled_functions = array(
"upload_history",
"do_upload",
"do_delete",
);
function __construct()
{
parent::__construct();
$this->load->model('mfile');
if (is_cli_client()) {
$this->var->view_dir = "file_plaintext";
} else {
$this->var->view_dir = "file";
}
}
function index()
{
if ($this->input->is_cli_request()) {
$this->load->library("../controllers/tools");
return $this->tools->index();
}
// Try to guess what the user would like to do.
$id = $this->uri->segment(1);
if (!empty($_FILES)) {
$this->do_upload();
} elseif ($id != "file" && $this->mfile->id_exists($id)) {
$this->_download();
} elseif ($id && $id != "file") {
$this->_non_existent();
} else {
$this->upload_form();
}
}
function _download()
{
$id = $this->uri->segment(1);
$lexer = urldecode($this->uri->segment(2));
$filedata = $this->mfile->get_filedata($id);
$file = $this->mfile->file($filedata['hash']);
if (!$this->mfile->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"];
// autodetect the lexer for highlighting if the URL contains a / after the ID (/ID/)
// /ID/lexer disables autodetection
$autodetect_lexer = !$lexer && substr_count(ltrim($this->uri->uri_string(), "/"), '/') >= 1;
if ($autodetect_lexer) {
$lexer = $this->mfile->autodetect_lexer($filedata["mimetype"], $filedata["filename"]);
}
// resolve aliases
// this is mainly used for compatibility
$lexer = $this->mfile->resolve_lexer_alias($lexer);
// create the qr code for /ID/
if ($lexer == "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();
}
$this->load->driver("ddownload");
// user wants the plain file
if ($lexer == 'plain') {
handle_etag($etag);
$this->ddownload->serveFile($file, $filedata["filename"], "text/plain");
exit();
}
if ($lexer == 'info') {
$this->_display_info($id);
return;
}
// if there is no mimetype mapping we can't highlight it
$can_highlight = $this->mfile->can_highlight($filedata["mimetype"]);
$filesize_too_big = filesize($file) > $this->config->item('upload_max_text_size');
if (!$can_highlight || $filesize_too_big || !$lexer) {
// 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", "Content-Security-Policy") as $header_name) {
header("$header_name: default-src 'none'; img-src *; media-src *; font-src *; style-src 'unsafe-inline' *; script-src 'none'; object-src *; frame-src 'none'; ");
}
handle_etag($etag);
$this->ddownload->serveFile($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($lexer);
$this->data['timeout'] = $this->mfile->get_timeout_string($id);
$this->data['lexers'] = $this->mfile->get_lexers();
$this->data['filedata'] = $filedata;
// highlight the file and cache the result
$highlit = cache_function($filedata['hash'].'_'.$lexer, 100, function() use ($file, $lexer){
$CI =& get_instance();
$ret = array();
if ($lexer == "rmd") {
ob_start();
echo '
'."\n";
echo '
'."\n";
echo '
'."\n";
passthru('perl '.FCPATH.'scripts/Markdown.pl '.escapeshellarg($file), $ret["return_value"]);
echo '
';
$ret["output"] = ob_get_clean();
} else {
$ret = $CI->_colorify($file, $lexer);
}
if ($ret["return_value"] != 0) {
$tmp = $CI->_colorify($file, "text");
$ret["output"] = $tmp["output"];
}
return $ret;
});
if ($highlit["return_value"] != 0) {
$this->data["error_message"] = "Error trying to process the file.
Either the lexer is unknown or something is broken.
Falling back to plain text.
";
}
// Don't use append_output because the output class does too
// much magic ({elapsed_time} and {memory_usage}).
// Direct echo puts us on the safe side.
echo $this->load->view($this->var->view_dir.'/html_header', $this->data, true);
echo $highlit["output"];
echo $this->load->view($this->var->view_dir.'/html_footer', $this->data, true);
}
private function _colorify($file, $lexer)
{
$return_value = 0;
$output = "";
$lines_to_remove = 0;
$output .= ''."\n";
$output .= '
'."\n";
ob_start();
if ($lexer == "ascii") {
passthru('ansi2html -p < '.escapeshellarg($file), $return_value);
// Last line is empty
$lines_to_remove = 1;
} else {
passthru('pygmentize -F codetagify -O encoding=guess,outencoding=utf8,stripnl=False -l '.escapeshellarg($lexer).' -f html '.escapeshellarg($file), $return_value);
// Last 2 items are "
" and ""
$lines_to_remove = 2;
}
$buf = ob_get_contents();
ob_end_clean();
$buf = explode("\n", $buf);
$line_count = count($buf);
for ($i = 1; $i <= $lines_to_remove; $i++) {
unset($buf[$line_count - $i]);
}
foreach ($buf as $key => $line) {
$line_number = $key + 1;
if ($key == 0) {
$line = str_replace("
", "", $line);
}
// Be careful not to add superflous whitespace here (we are in a )
$output .= "";
}
$output .= "
";
$output .= "
";
return array(
"return_value" => $return_value,
"output" => $output
);
}
function _display_info($id)
{
$this->data["title"] .= " - Info $id";
$this->data["filedata"] = $this->mfile->get_filedata($id);
$this->data["id"] = $id;
$this->data['timeout'] = $this->mfile->get_timeout_string($id);
$this->load->view('header', $this->data);
$this->load->view($this->var->view_dir.'/file_info', $this->data);
$this->load->view('footer', $this->data);
}
function _non_existent()
{
$this->data["title"] .= " - Not Found";
$this->output->set_status_header(404);
$this->load->view('header', $this->data);
$this->load->view($this->var->view_dir.'/non_existent', $this->data);
$this->load->view('footer', $this->data);
}
function _show_url($ids, $lexer)
{
$redirect = false;
if (!$this->muser->logged_in()) {
$this->muser->require_session();
// keep the upload but require the user to login
$this->session->set_userdata("last_upload", array(
"ids" => $ids,
"lexer" => $lexer
));
$this->session->set_flashdata("uri", "file/claim_id");
$this->muser->require_access("basic");
}
foreach ($ids as $id) {
if ($lexer) {
$this->data['urls'][] = site_url($id).'/'.$lexer;
} else {
$this->data['urls'][] = site_url($id).'/';
if (count($ids) == 1) {
$filedata = $this->mfile->get_filedata($id);
$file = $this->mfile->file($filedata['hash']);
$type = $filedata['mimetype'];
$lexer = $this->mfile->should_highlight($type);
// If we detected a highlightable file redirect,
// otherwise show the URL because browsers would just show a DL dialog
if ($lexer) {
$redirect = true;
}
}
}
}
if (static_storage("response_type") == "json") {
return send_json_reply($this->data["urls"]);
}
if (is_cli_client()) {
$redirect = false;
}
if ($redirect && count($ids) == 1) {
redirect($this->data['urls'][0], "location", 303);
} else {
$this->load->view('header', $this->data);
$this->load->view($this->var->view_dir.'/show_url', $this->data);
$this->load->view('footer', $this->data);
}
}
function client()
{
$this->data['title'] .= ' - Client';
if (file_exists(FCPATH.'data/client/latest')) {
$this->var->latest_client = trim(file_get_contents(FCPATH.'data/client/latest'));
$this->data['client_link'] = base_url().'data/client/fb-'.$this->var->latest_client.'.tar.gz';
} else {
$this->data['client_link'] = false;
}
$this->data['client_link_dir'] = base_url().'data/client/';
$this->data['client_link_deb'] = base_url().'data/client/deb/';
$this->data['client_link_slackware'] = base_url().'data/client/slackware/';
if (preg_match('#^https?://(.*?)/.*$#', site_url(), $matches) === 1) {
$this->data["domain"] = $matches[1];
} else {
$this->data["domain"] = "unknown domain";
}
if (!is_cli_client()) {
$this->load->view('header', $this->data);
}
$this->load->view($this->var->view_dir.'/client', $this->data);
if (!is_cli_client()) {
$this->load->view('footer', $this->data);
}
}
function upload_form()
{
$this->data['title'] .= ' - Upload';
$this->data['small_upload_size'] = $this->config->item('small_upload_size');
$this->data['max_upload_size'] = $this->config->item('upload_max_size');
$this->data['upload_max_age'] = $this->config->item('upload_max_age')/60/60/24;
$this->data['username'] = $this->muser->get_username();
$repaste_id = $this->input->get("repaste");
if ($repaste_id) {
$filedata = $this->mfile->get_filedata($repaste_id);
if ($filedata !== false && $this->mfile->can_highlight($filedata["mimetype"])) {
$this->data["textarea_content"] = file_get_contents($this->mfile->file($filedata["hash"]));
}
}
$this->load->view('header', $this->data);
$this->load->view($this->var->view_dir.'/upload_form', $this->data);
if (is_cli_client()) {
$this->client();
}
$this->load->view('footer', $this->data);
}
// Allow CLI clients to query the server for the maxium filesize so they can
// stop the upload before wasting time and bandwith
function get_max_size()
{
echo $this->config->item('upload_max_size');
}
function thumbnail()
{
$id = $this->uri->segment(3);
if (!$this->mfile->valid_id($id)) {
return $this->_non_existent();
}
$etag = "$id-thumb";
handle_etag($etag);
$thumb_size = 150;
$filedata = $this->mfile->get_filedata($id);
if (!$filedata) {
show_error("Failed to get file data");
}
$cache_key = $filedata['hash'].'_thumb_'.$thumb_size;
$thumb = cache_function($cache_key, 100, function() use ($id, $thumb_size){
$CI =& get_instance();
$thumb = $CI->mfile->makeThumb($id, $thumb_size, IMAGETYPE_JPEG);
if ($thumb === false) {
show_error("Failed to generate thumbnail");
}
return $thumb;
});
$this->output->set_header("Cache-Control:max-age=31536000, public");
$this->output->set_header("Expires: ".date("r", time() + 365 * 24 * 60 * 60));
$this->output->set_content_type("image/jpeg");
$this->output->set_output($thumb);
}
function upload_history_thumbnails()
{
$this->muser->require_access();
$user = $this->muser->get_userid();
$query = $this->db->query("
SELECT `id`, `filename`, `mimetype`, `date`, `hash`, `filesize`
FROM files
WHERE user = ?
AND mimetype IN ('image/jpeg', 'image/png', 'image/gif')
ORDER BY date DESC
", array($user))->result_array();
foreach($query as $key => $item) {
if (!$this->mfile->valid_id($item["id"])) {
unset($query[$key]);
continue;
}
$filesize = format_bytes($item["filesize"]);
$dimensions = $this->mfile->image_dimension($this->mfile->file($item["hash"]));
$upload_date = date("r", $item["date"]);
$query[$key]["filesize"] = $filesize;
$query[$key]["tooltip"] = "
${item["id"]} - $filesizeContact info not available.
';
}
$this->load->view('header', $this->data);
$this->load->view('contact', $this->data);
$this->load->view('footer', $this->data);
}
/* Functions below this comment can only be run via the CLI
* `php index.php file