files: Provide unified download script
This script adds a single download script, 'file.php', which provides access to files of all types to all users. It uses a HMAC signature system with an expires time, which allows signed URLs to be sent to external users. This also fixes an issue with the Http::cacheable() method, where the last-modified and Etag headers were not properly compared, which resulted in permanent cache misses by the client.
This commit is contained in:
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
attachment.php
|
||||
|
||||
Attachments interface for clients.
|
||||
Clients should never see the dir paths.
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2013 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
require('secure.inc.php');
|
||||
require_once(INCLUDE_DIR.'class.attachment.php');
|
||||
//Basic checks
|
||||
if(!$thisclient
|
||||
|| !$_GET['id']
|
||||
|| !$_GET['h']
|
||||
|| !($attachment=Attachment::lookup($_GET['id']))
|
||||
|| !($file=$attachment->getFile()))
|
||||
Http::response(404, __('Unknown or invalid file'));
|
||||
|
||||
//Validate session access hash - we want to make sure the link is FRESH! and the user has access to the parent ticket!!
|
||||
$vhash=md5($attachment->getFileId().session_id().strtolower($file->getKey()));
|
||||
if(strcasecmp(trim($_GET['h']),$vhash)
|
||||
|| !($ticket=$attachment->getTicket())
|
||||
|| !$ticket->checkUserAccess($thisclient))
|
||||
Http::response(404, __('Unknown or invalid file'));
|
||||
//Download the file..
|
||||
$file->download();
|
||||
|
||||
?>
|
||||
39
file.php
Normal file
39
file.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
file.php
|
||||
|
||||
File download facilitator for clients
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Jared Hancock <jared@osticket.com>
|
||||
Copyright (c) 2006-2014 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
require('client.inc.php');
|
||||
require_once(INCLUDE_DIR.'class.file.php');
|
||||
|
||||
//Basic checks
|
||||
if (!$_GET['key']
|
||||
|| !$_GET['signature']
|
||||
|| !$_GET['expires']
|
||||
|| !($file = AttachmentFile::lookup($_GET['key']))
|
||||
) {
|
||||
Http::response(404, __('Unknown or invalid file'));
|
||||
}
|
||||
|
||||
// Validate session access hash - we want to make sure the link is FRESH!
|
||||
// and the user has access to the parent ticket!!
|
||||
if ($file->verifySignature($_GET['signature'], $_GET['expires'])) {
|
||||
if (($s = @$_GET['s']) && strpos($file->getType(), 'image/') === 0)
|
||||
$file->display($s);
|
||||
|
||||
// Download the file..
|
||||
$file->download(@$_GET['disposition'] ?: false, $_GET['expires']);
|
||||
}
|
||||
// else
|
||||
Http::response(404, __('Unknown or invalid file'));
|
||||
31
image.php
31
image.php
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
image.php
|
||||
|
||||
Simply downloads the file...on hash validation as follows;
|
||||
|
||||
* Hash must be 64 chars long.
|
||||
* First 32 chars is the perm. file hash
|
||||
* Next 32 chars is md5(file_id.session_id().file_hash)
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2013 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
require('client.inc.php');
|
||||
require_once(INCLUDE_DIR.'class.file.php');
|
||||
$h=trim($_GET['h']);
|
||||
//basic checks
|
||||
if(!$h || strlen($h)!=64 //32*2
|
||||
|| !($file=AttachmentFile::lookup(substr($h,0,32))) //first 32 is the file hash.
|
||||
|| strcasecmp($h, $file->getDownloadHash())) //next 32 is file id + session hash.
|
||||
Http::response(404, __('Unknown or invalid file'));
|
||||
|
||||
$file->display();
|
||||
?>
|
||||
@@ -129,7 +129,7 @@ class DraftAjaxAPI extends AjaxController {
|
||||
|
||||
echo JsonDataEncoder::encode(array(
|
||||
'content_id' => 'cid:'.$f->getKey(),
|
||||
'filelink' => sprintf('image.php?h=%s', $f->getDownloadHash())
|
||||
'filelink' => $f->getDownloadUrl(false, 'inline'),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ class DraftAjaxAPI extends AjaxController {
|
||||
);
|
||||
while (list($id, $type) = db_fetch_row($res)) {
|
||||
$f = AttachmentFile::lookup($id);
|
||||
$url = 'image.php?h='.$f->getDownloadHash();
|
||||
$url = $f->getDownloadUrl();
|
||||
$files[] = array(
|
||||
'thumb'=>$url.'&s=128',
|
||||
'image'=>$url,
|
||||
|
||||
@@ -171,15 +171,15 @@ class GenericAttachments {
|
||||
function _getList($separate=false, $inlines=false) {
|
||||
if(!isset($this->attachments)) {
|
||||
$this->attachments = array();
|
||||
$sql='SELECT f.id, f.size, f.`key`, f.name, a.inline '
|
||||
$sql='SELECT f.id, f.size, f.`key`, f.signature, f.name, a.inline '
|
||||
.' FROM '.FILE_TABLE.' f '
|
||||
.' INNER JOIN '.ATTACHMENT_TABLE.' a ON(f.id=a.file_id) '
|
||||
.' WHERE a.`type`='.db_input($this->getType())
|
||||
.' AND a.object_id='.db_input($this->getId());
|
||||
if(($res=db_query($sql)) && db_num_rows($res)) {
|
||||
while($rec=db_fetch_array($res)) {
|
||||
$rec['download'] = AttachmentFile::getDownloadForIdAndKey(
|
||||
$rec['id'], $rec['key']);
|
||||
$rec['download_url'] = AttachmentFile::generateDownloadUrl(
|
||||
$rec['id'], $rec['key'], $rec['signature']);
|
||||
$this->attachments[] = $rec;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ class Draft {
|
||||
}
|
||||
|
||||
function setBody($body) {
|
||||
// Change image.php urls back to content-id's
|
||||
// Change file.php urls back to content-id's
|
||||
$body = Format::sanitize($body, false);
|
||||
$this->ht['body'] = $body;
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ class FAQ {
|
||||
function getQuestion() { return $this->ht['question']; }
|
||||
function getAnswer() { return $this->ht['answer']; }
|
||||
function getAnswerWithImages() {
|
||||
return Format::viewableImages($this->ht['answer'], ROOT_PATH.'image.php');
|
||||
return Format::viewableImages($this->ht['answer']);
|
||||
}
|
||||
function getSearchableAnswer() {
|
||||
return ThreadBody::fromFormattedText($this->ht['answer'], 'html')
|
||||
@@ -198,12 +198,11 @@ class FAQ {
|
||||
if(($attachments=$this->attachments->getSeparates())) {
|
||||
foreach($attachments as $attachment ) {
|
||||
/* The h key must match validation in file.php */
|
||||
$hash=$attachment['key'].md5($attachment['id'].session_id().strtolower($attachment['key']));
|
||||
if($attachment['size'])
|
||||
$size=sprintf(' <small>(<i>%s</i>)</small>',Format::file_size($attachment['size']));
|
||||
|
||||
$str.=sprintf('<a class="Icon file no-pjax" href="file.php?h=%s" target="%s">%s</a>%s %s',
|
||||
$hash, $target, Format::htmlchars($attachment['name']), $size, $separator);
|
||||
$str.=sprintf('<a class="Icon file no-pjax" href="%s" target="%s">%s</a>%s %s',
|
||||
$attachment['download_url'], $target, Format::htmlchars($attachment['name']), $size, $separator);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,19 +107,6 @@ class AttachmentFile {
|
||||
return $this->ht['created'];
|
||||
}
|
||||
|
||||
static function getDownloadForIdAndKey($id, $key) {
|
||||
return strtolower($key . md5($id.session_id().strtolower($key)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a signature that can be sent to scp/file.php?h= in order to
|
||||
* download this file
|
||||
*/
|
||||
function getDownloadHash() {
|
||||
return self::getDownloadForIdAndKey($this->getId(), $this->getKey());
|
||||
}
|
||||
|
||||
function open() {
|
||||
return FileStorageBackend::getInstance($this);
|
||||
}
|
||||
@@ -200,13 +187,70 @@ class AttachmentFile {
|
||||
exit();
|
||||
}
|
||||
|
||||
function download() {
|
||||
function getDownloadUrl($minage=false, $disposition=false, $handler=false) {
|
||||
// XXX: Drop this when AttachmentFile goes to ORM
|
||||
return static::generateDownloadUrl($this->getId(),
|
||||
strtolower($this->getKey()), $this->getSignature(), $minage,
|
||||
$disposition, $handler);
|
||||
}
|
||||
|
||||
static function generateDownloadUrl($id, $key, $hash, $minage=false,
|
||||
$disposition=false, $handler=false
|
||||
) {
|
||||
// Expire at the nearest midnight, allowing at least 12 hours access
|
||||
$minage = $minage ?: 43200;
|
||||
$gmnow = Misc::gmtime() + $minage;
|
||||
$expires = $gmnow + 86400 - ($gmnow % 86400);
|
||||
|
||||
// Generate a signature based on secret content
|
||||
$signature = static::_genUrlSignature($id, $key, $hash, $expires);
|
||||
|
||||
$handler = $handler ?: ROOT_PATH . 'file.php';
|
||||
|
||||
// Return sanitized query string
|
||||
$args = array(
|
||||
'key' => $key,
|
||||
'expires' => $expires,
|
||||
'signature' => $signature,
|
||||
);
|
||||
|
||||
if ($disposition)
|
||||
$args['disposition'] = $disposition;
|
||||
|
||||
return $handler . '?' . http_build_query($args);
|
||||
}
|
||||
|
||||
function verifySignature($signature, $expires) {
|
||||
$gmnow = Misc::gmtime();
|
||||
if ($expires < $gmnow)
|
||||
return false;
|
||||
|
||||
$check = static::_genUrlSignature($this->getId(), $this->getKey(),
|
||||
$this->getSignature(), $expires);
|
||||
return $signature == $check;
|
||||
}
|
||||
|
||||
static function _genUrlSignature($id, $key, $signature, $expires) {
|
||||
$pieces = array(
|
||||
'Host='.$_SERVER['HTTP_HOST'],
|
||||
'Path='.ROOT_PATH,
|
||||
'Id='.$id,
|
||||
'Key='.strtolower($key),
|
||||
'Hash='.$signature,
|
||||
'Expires='.$expires,
|
||||
);
|
||||
return hash_hmac('sha1', implode("\n", $pieces), SECRET_SALT);
|
||||
}
|
||||
|
||||
function download($disposition=false, $expires=false) {
|
||||
$disposition = $disposition ?: 'attachment';
|
||||
$bk = $this->open();
|
||||
if ($bk->sendRedirectUrl('inline'))
|
||||
if ($bk->sendRedirectUrl($disposition))
|
||||
return;
|
||||
$this->makeCacheable();
|
||||
$ttl = ($expires) ? $expires - Misc::gmtime() : false;
|
||||
$this->makeCacheable($ttl);
|
||||
Http::download($this->getName(), $this->getType() ?: 'application/octet-stream',
|
||||
null, 'inline');
|
||||
null, $disposition);
|
||||
header('Content-Length: '.$this->getSize());
|
||||
$this->sendData(false);
|
||||
exit();
|
||||
|
||||
@@ -259,9 +259,10 @@ class Format {
|
||||
}
|
||||
|
||||
function localizeInlineImages($text) {
|
||||
// Change image.php urls back to content-id's
|
||||
return preg_replace('/image\\.php\\?h=([\\w.-]{32})\\w{32}/',
|
||||
'cid:$1', $text);
|
||||
// Change file.php urls back to content-id's
|
||||
return preg_replace(
|
||||
'/src="(?:\/[^"]+?)?\/file\\.php\\?(?:\w+=[^&]+&(?:amp;)?)*?key=([^&]+)[^"]*/',
|
||||
'src="cid:$1', $text);
|
||||
}
|
||||
|
||||
function sanitize($text, $striptags=false) {
|
||||
@@ -430,14 +431,14 @@ class Format {
|
||||
}
|
||||
|
||||
|
||||
function viewableImages($html, $script='image.php') {
|
||||
function viewableImages($html, $script=false) {
|
||||
return preg_replace_callback('/"cid:([\w._-]{32})"/',
|
||||
function($match) use ($script) {
|
||||
$hash = $match[1];
|
||||
if (!($file = AttachmentFile::lookup($hash)))
|
||||
return $match[0];
|
||||
return sprintf('"%s?h=%s" data-cid="%s"',
|
||||
$script, $file->getDownloadHash(), $match[1]);
|
||||
return sprintf('"%s" data-cid="%s"',
|
||||
$file->getDownloadUrl(false, 'inline', $script), $match[1]);
|
||||
}, $html);
|
||||
}
|
||||
|
||||
|
||||
@@ -1643,10 +1643,8 @@ class FileUploadField extends FormField {
|
||||
function display($value) {
|
||||
$links = array();
|
||||
foreach ($this->getFiles() as $f) {
|
||||
$hash = strtolower($f['key']
|
||||
. md5($f['id'].session_id().strtolower($f['key'])));
|
||||
$links[] = sprintf('<a class="no-pjax" href="file.php?h=%s">%s</a>',
|
||||
$hash, Format::htmlchars($f['name']));
|
||||
$links[] = sprintf('<a class="no-pjax" href="%s">%s</a>',
|
||||
Format::htmlchars($f['download_url']), Format::htmlchars($f['name']));
|
||||
}
|
||||
return implode('<br/>', $links);
|
||||
}
|
||||
@@ -2092,6 +2090,7 @@ class FileUploadWidget extends Widget {
|
||||
'name' => $file->getName(),
|
||||
'type' => $file->getType(),
|
||||
'size' => $file->getSize(),
|
||||
'download_url' => $file->getDownloadUrl(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,14 +63,16 @@ class Http {
|
||||
|
||||
function cacheable($etag, $modified, $ttl=3600) {
|
||||
// Thanks, http://stackoverflow.com/a/1583753/1025836
|
||||
$last_modified = Misc::db2gmtime($modified);
|
||||
header("Last-Modified: ".date('D, d M y H:i:s', $last_modified)." GMT", false);
|
||||
// Timezone doesn't matter here — but the time needs to be
|
||||
// consistent round trip to the browser and back.
|
||||
$last_modified = strtotime($modified." GMT");
|
||||
header("Last-Modified: ".date('D, d M Y H:i:s', $last_modified)." GMT", false);
|
||||
header('ETag: "'.$etag.'"');
|
||||
header("Cache-Control: private, max-age=$ttl");
|
||||
header('Expires: ' . gmdate(DATE_RFC822, time() + $ttl)." GMT");
|
||||
header('Expires: ' . gmdate('D, d M Y H:i:s', Misc::gmtime() + $ttl)." GMT");
|
||||
header('Pragma: private');
|
||||
if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified ||
|
||||
@trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) {
|
||||
@trim($_SERVER['HTTP_IF_NONE_MATCH'], '"') == $etag) {
|
||||
header("HTTP/1.1 304 Not Modified");
|
||||
exit();
|
||||
}
|
||||
@@ -97,9 +99,8 @@ class Http {
|
||||
|
||||
function download($filename, $type, $data=null, $disposition='attachment') {
|
||||
header('Pragma: private');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
header('Cache-Control: private');
|
||||
header('Cache-Control: private', false);
|
||||
header('Content-Type: '.$type);
|
||||
header(sprintf('Content-Disposition: %s; %s',
|
||||
$disposition,
|
||||
|
||||
@@ -71,7 +71,7 @@ class Page {
|
||||
return $this->ht['body'];
|
||||
}
|
||||
function getBodyWithImages() {
|
||||
return Format::viewableImages($this->getBody(), ROOT_PATH.'image.php');
|
||||
return Format::viewableImages($this->getBody());
|
||||
}
|
||||
|
||||
function getNotes() {
|
||||
|
||||
@@ -579,7 +579,7 @@ Class ThreadEntry {
|
||||
return $this->attachments;
|
||||
|
||||
//XXX: inner join the file table instead?
|
||||
$sql='SELECT a.attach_id, f.id as file_id, f.size, lower(f.`key`) as file_hash, f.name, a.inline '
|
||||
$sql='SELECT a.attach_id, f.id as file_id, f.size, lower(f.`key`) as file_hash, f.`signature` as file_sig, f.name, a.inline '
|
||||
.' FROM '.FILE_TABLE.' f '
|
||||
.' INNER JOIN '.TICKET_ATTACHMENT_TABLE.' a ON(f.id=a.file_id) '
|
||||
.' WHERE a.ticket_id='.db_input($this->getTicketId())
|
||||
@@ -587,19 +587,21 @@ Class ThreadEntry {
|
||||
|
||||
$this->attachments = array();
|
||||
if(($res=db_query($sql)) && db_num_rows($res)) {
|
||||
while($rec=db_fetch_array($res))
|
||||
while($rec=db_fetch_array($res)) {
|
||||
$rec['download_url'] = AttachmentFile::generateDownloadUrl(
|
||||
$rec['file_id'], $rec['file_hash'], $rec['file_sig']);
|
||||
$this->attachments[] = $rec;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->attachments;
|
||||
}
|
||||
|
||||
function getAttachmentUrls($script='image.php') {
|
||||
function getAttachmentUrls() {
|
||||
$json = array();
|
||||
foreach ($this->getAttachments() as $att) {
|
||||
$json[$att['file_hash']] = array(
|
||||
'download_url' => sprintf('attachment.php?id=%d&h=%s', $att['attach_id'],
|
||||
strtolower(md5($att['file_id'].session_id().$att['file_hash']))),
|
||||
'download_url' => $att['download_url'],
|
||||
'filename' => $att['name'],
|
||||
);
|
||||
}
|
||||
@@ -612,14 +614,12 @@ Class ThreadEntry {
|
||||
foreach($this->getAttachments() as $attachment ) {
|
||||
if ($attachment['inline'])
|
||||
continue;
|
||||
/* The hash can be changed but must match validation in @file */
|
||||
$hash=md5($attachment['file_id'].session_id().$attachment['file_hash']);
|
||||
$size = '';
|
||||
if($attachment['size'])
|
||||
$size=sprintf('<em>(%s)</em>', Format::file_size($attachment['size']));
|
||||
|
||||
$str.=sprintf('<a class="Icon file no-pjax" href="%s?id=%d&h=%s" target="%s">%s</a>%s %s',
|
||||
$file, $attachment['attach_id'], $hash, $target, Format::htmlchars($attachment['name']), $size, $separator);
|
||||
$str.=sprintf('<a class="Icon file no-pjax" href="%s" target="%s">%s</a>%s %s',
|
||||
$attachment['download_url'], $target, Format::htmlchars($attachment['name']), $size, $separator);
|
||||
}
|
||||
|
||||
return $str;
|
||||
|
||||
@@ -135,7 +135,7 @@ $pages = Page::getPages();
|
||||
echo $logo->getId(); ?>" <?php
|
||||
if ($logo->getId() == $current)
|
||||
echo 'checked="checked"'; ?>/>
|
||||
<img src="image.php?h=<?php echo $logo->getDownloadHash(); ?>"
|
||||
<img src="<?php echo $logo->getDownloadUrl(); ?>"
|
||||
alt="Custom Logo" valign="middle"
|
||||
style="box-shadow: 0 0 0.5em rgba(0,0,0,0.5);
|
||||
margin: 0.5em; height: 5em;
|
||||
|
||||
@@ -180,11 +180,12 @@
|
||||
}
|
||||
if (file.id)
|
||||
filenode.data('fileId', file.id);
|
||||
if (file.download)
|
||||
if (file.download_url) {
|
||||
filenode.find('.filename').prepend(
|
||||
$('<a class="no-pjax" target="_blank"></a>').text(file.name)
|
||||
.attr('href', 'file.php?h='+escape(file.download))
|
||||
.attr('href', file.download_url)
|
||||
);
|
||||
}
|
||||
else
|
||||
filenode.find('.filename').prepend($('<span>').text(file.name));
|
||||
this.$element.parent().find('.files').append(filenode);
|
||||
|
||||
30
kb/file.php
30
kb/file.php
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
file.php
|
||||
|
||||
Simply downloads the file...on hash validation as follows;
|
||||
|
||||
* Hash must be 64 chars long.
|
||||
* First 32 chars is the perm. file hash
|
||||
* Next 32 chars is md5(file_id.session_id().file_hash)
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2013 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
require('kb.inc.php');
|
||||
require_once(INCLUDE_DIR.'class.file.php');
|
||||
$h=trim($_GET['h']);
|
||||
//basic checks
|
||||
if(!$h || strlen($h)!=64 //32*2
|
||||
|| !($file=AttachmentFile::lookup(substr($h,0,32))) //first 32 is the file hash.
|
||||
|| strcasecmp($h, $file->getDownloadHash())) //next 32 is file id + session hash.
|
||||
Http::response(404, __('Unknown or invalid file'));
|
||||
|
||||
$file->download();
|
||||
?>
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
attachment.php
|
||||
|
||||
Handles attachment downloads & access validation.
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2013 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
require('staff.inc.php');
|
||||
require_once(INCLUDE_DIR.'class.attachment.php');
|
||||
|
||||
//Basic checks
|
||||
if(!$thisstaff || !$_GET['id'] || !$_GET['h']
|
||||
|| !($attachment=Attachment::lookup($_GET['id']))
|
||||
|| !($file=$attachment->getFile()))
|
||||
Http::response(404, __('Unknown or invalid file'));
|
||||
|
||||
//Validate session access hash - we want to make sure the link is FRESH! and the user has access to the parent ticket!!
|
||||
$vhash=md5($attachment->getFileId().session_id().strtolower($file->getKey()));
|
||||
if(strcasecmp(trim($_GET['h']),$vhash) || !($ticket=$attachment->getTicket()) || !$ticket->checkStaffAccess($thisstaff)) die(__('Access Denied'));
|
||||
|
||||
//Download the file..
|
||||
$file->download();
|
||||
?>
|
||||
30
scp/file.php
30
scp/file.php
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
file.php
|
||||
|
||||
Simply downloads the file...on hash validation as follows;
|
||||
|
||||
* Hash must be 64 chars long.
|
||||
* First 32 chars is the perm. file hash
|
||||
* Next 32 chars is md5(file_id.session_id().file_hash)
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2013 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
require('staff.inc.php');
|
||||
require_once(INCLUDE_DIR.'class.file.php');
|
||||
$h=trim($_GET['h']);
|
||||
//basic checks
|
||||
if(!$h || strlen($h)!=64 //32*2
|
||||
|| !($file=AttachmentFile::lookup(substr($h,0,32))) //first 32 is the file hash.
|
||||
|| strcasecmp($file->getDownloadHash(), $h)) //next 32 is file id + session hash.
|
||||
Http::response(404, __('Unknown or invalid file'));
|
||||
|
||||
$file->download();
|
||||
?>
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
/*********************************************************************
|
||||
image.php
|
||||
|
||||
Simply downloads the file...on hash validation as follows;
|
||||
|
||||
* Hash must be 64 chars long.
|
||||
* First 32 chars is the perm. file hash
|
||||
* Next 32 chars is md5(file_id.session_id().file_hash)
|
||||
|
||||
Peter Rotich <peter@osticket.com>
|
||||
Copyright (c) 2006-2013 osTicket
|
||||
http://www.osticket.com
|
||||
|
||||
Released under the GNU General Public License WITHOUT ANY WARRANTY.
|
||||
See LICENSE.TXT for details.
|
||||
|
||||
vim: expandtab sw=4 ts=4 sts=4:
|
||||
**********************************************************************/
|
||||
|
||||
require('staff.inc.php');
|
||||
require_once(INCLUDE_DIR.'class.file.php');
|
||||
$h=trim($_GET['h']);
|
||||
//basic checks
|
||||
if(!$h || strlen($h)!=64 //32*2
|
||||
|| !($file=AttachmentFile::lookup(substr($h,0,32))) //first 32 is the file hash.
|
||||
|| strcasecmp($h, $file->getDownloadHash())) //next 32 is file id + session hash.
|
||||
Http::response(404, __('Unknown or invalid file'));
|
||||
|
||||
if ($_GET['s'] && is_numeric($_GET['s']))
|
||||
$file->display($_GET['s']);
|
||||
else
|
||||
$file->display();
|
||||
?>
|
||||
Reference in New Issue
Block a user