diff --git a/file.php b/file.php index 33ffec5ff1cf038dbfb999b64ce8af5d1c1db3b7..994b77a0c2256a0218a27ffebb01b1c7d77a344b 100644 --- a/file.php +++ b/file.php @@ -26,21 +26,34 @@ if (!$_GET['key'] Http::response(404, __('Unknown or invalid file')); } -// Enforce security settings -if ($cfg->isAuthRequiredForFiles() && !$thisclient) { - if (!($U = StaffAuthenticationBackend::getUser())) { - // Try and determine if a staff is viewing this page - if (strpos($_SERVER['HTTP_REFERRER'], ROOT_PATH . 'scp/') !== false) { - $_SESSION['_staff']['auth']['dest'] = - '/' . ltrim($_SERVER['REQUEST_URI'], '/'); - Http::redirect(ROOT_PATH.'scp/login.php'); - } - else { - require 'secure.inc.php'; - } +// Get the object type the file is attached to +$type = ''; +if ($_GET['id'] + && ($a=$file->attachments->findFirst(array( + 'id' => $_GET['id'])))) + $type = $a->type; + +// Enforce security settings if enabled. +if ($cfg->isAuthRequiredForFiles() + // FAQ & Page files allowed without login. + && !in_array($type, ['P', 'F']) + // Check user login + && !$thisuser + // Check staff login + && !StaffAuthenticationBackend::getUser() + ) { + + // Try and determine if an agent is viewing the page / file + if (strpos($_SERVER['HTTP_REFERRER'], ROOT_PATH . 'scp/') !== false) { + $_SESSION['_staff']['auth']['dest'] = + '/' . ltrim($_SERVER['REQUEST_URI'], '/'); + Http::redirect(ROOT_PATH.'scp/login.php'); + } else { + require 'secure.inc.php'; } } + // 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'])) { diff --git a/include/ajax.draft.php b/include/ajax.draft.php index e1bb78a0435b619f9bba99973b806543e6fd4a23..43e7f98b8651001ac5416b35c6f0563f84821f49 100644 --- a/include/ajax.draft.php +++ b/include/ajax.draft.php @@ -133,7 +133,8 @@ class DraftAjaxAPI extends AjaxController { 'content_id' => 'cid:'.$f->getKey(), // Return draft_id to connect the auto draft creation 'draft_id' => $draft->getId(), - 'filelink' => $f->getDownloadUrl(false, 'inline'), + 'filelink' => $f->getDownloadUrl( + ['type' => 'D', 'deposition' => 'inline']), )); } @@ -339,14 +340,14 @@ class DraftAjaxAPI extends AjaxController { && ($object = $thread->getObject()) && ($thisstaff->canAccess($object)) ) { - $union = ' UNION SELECT f.id, a.`type`, a.`name` FROM '.THREAD_TABLE.' t + $union = ' UNION SELECT f.id, a.id as aid, a.`type`, a.`name` FROM '.THREAD_TABLE.' t JOIN '.THREAD_ENTRY_TABLE.' th ON (th.thread_id = t.id) JOIN '.ATTACHMENT_TABLE.' a ON (a.object_id = th.id AND a.`type` = \'H\') JOIN '.FILE_TABLE.' f ON (a.file_id = f.id) WHERE a.`inline` = 1 AND t.id='.db_input($_GET['threadId']); } - $sql = 'SELECT distinct f.id, COALESCE(a.type, f.ft), a.`name` FROM '.FILE_TABLE + $sql = 'SELECT distinct f.id, a.id as aid, COALESCE(a.type, f.ft), a.`name` FROM '.FILE_TABLE .' f LEFT JOIN '.ATTACHMENT_TABLE.' a ON (a.file_id = f.id) WHERE ((a.`type` IN (\'C\', \'F\', \'T\', \'P\') AND a.`inline` = 1) OR f.ft = \'L\')' .' AND f.`type` LIKE \'image/%\''; @@ -354,9 +355,11 @@ class DraftAjaxAPI extends AjaxController { Http::response(500, 'Unable to lookup files'); $files = array(); - while (list($id, $type, $name) = db_fetch_row($res)) { - $f = AttachmentFile::lookup((int) $id); - $url = $f->getDownloadUrl(); + while (list($id, $aid, $type, $name) = db_fetch_row($res)) { + if (!($f = AttachmentFile::lookup((int) $id))) + continue; + + $url = $f->getDownloadUrl(['id' => $aid]); $files[] = array( // Don't send special sizing for thread items 'cause they // should be cached already by the client diff --git a/include/class.category.php b/include/class.category.php index 3eeedfd144b09721f4004a31a3aa26c9be2e5108..c4b1ff0d1ab8f2d2c40db18c8ff5fc5980facccc 100644 --- a/include/class.category.php +++ b/include/class.category.php @@ -55,7 +55,9 @@ class Category extends VerySimpleModel { return $count; } function getDescription() { return $this->description; } - function getDescriptionWithImages() { return Format::viewableImages($this->description); } + function getDescriptionWithImages() { + return Format::viewableImages($this->description); + } function getNotes() { return $this->notes; } function getCreateDate() { return $this->created; } function getUpdateDate() { return $this->updated; } diff --git a/include/class.faq.php b/include/class.faq.php index 4eb1c2d83df46c4953ed0712c1cb03c5a8d913ba..555fd3aee7f6f01fb485259e338c13c394dc826b 100644 --- a/include/class.faq.php +++ b/include/class.faq.php @@ -74,7 +74,7 @@ class FAQ extends VerySimpleModel { function getQuestion() { return $this->question; } function getAnswer() { return $this->answer; } function getAnswerWithImages() { - return Format::viewableImages($this->answer); + return Format::viewableImages($this->answer, ['type' => 'F']); } function getTeaser() { return Format::truncate(Format::striptags($this->answer), 150); @@ -194,7 +194,8 @@ class FAQ extends VerySimpleModel { return $this->_getLocal('answer', $lang); } function getLocalAnswerWithImages($lang=false) { - return Format::viewableImages($this->getLocalAnswer($lang)); + return Format::viewableImages($this->getLocalAnswer($lang), + ['type' => 'F']); } function _getLocal($what, $lang=false) { if (!$lang) { diff --git a/include/class.file.php b/include/class.file.php index bcdb9e0ed68f0ce7a944a9ae866ea5626dd4b90b..ed7adb93633fab2a1bd980ea3bde942be6ff9035 100644 --- a/include/class.file.php +++ b/include/class.file.php @@ -184,25 +184,31 @@ class AttachmentFile extends VerySimpleModel { exit(); } - function getDownloadUrl($minage=false, $disposition=false, $handler=false) { - // XXX: Drop this when AttachmentFile goes to ORM + function getDownloadUrl($options=array()) { + // Add attachment ref id if object type is set + if (isset($options['type']) + && !isset($options['id']) + && ($a=$this->attachments->findFirst(array( + 'type' => $options['type'])))) + $options['id'] = $a->getId(); + return static::generateDownloadUrl($this->getId(), - strtolower($this->getKey()), $this->getSignature(), $minage, - $disposition, $handler); + strtolower($this->getKey()), $this->getSignature(), + $options); } - 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; + static function generateDownloadUrl($id, $key, $hash, $options = array()) { + + // Expire at the nearest midnight, allow at least12 hrs access + $minage = @$options['minage'] ?: 43200; + $gmnow = Misc::gmtime() + $options['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'; + // Handler / base url + $handler = @$options['handler'] ?: ROOT_PATH . 'file.php'; // Return sanitized query string $args = array( @@ -211,10 +217,13 @@ class AttachmentFile extends VerySimpleModel { 'signature' => $signature, ); - if ($disposition) - $args['disposition'] = $disposition; + if (isset($options['disposition'])) + $args['disposition'] = $options['disposition']; + + if (isset($options['id'])) + $args['id'] = $options['id']; - return $handler . '?' . http_build_query($args); + return sprintf('%s?%s', $handler, http_build_query($args)); } function verifySignature($signature, $expires) { diff --git a/include/class.format.php b/include/class.format.php index 50c8a7fdf144c12d23f9f59ff4c1b3dc87cd4609..7d7d7d9225a76aba2f3d7f72bee7084c75ac9c1f 100644 --- a/include/class.format.php +++ b/include/class.format.php @@ -455,14 +455,17 @@ class Format { } - function viewableImages($html, $script=false) { + function viewableImages($html, $options=array()) { $cids = $images = array(); + $options +=array( + 'deposition' => 'inline'); return preg_replace_callback('/"cid:([\w._-]{32})"/', - function($match) use ($script, $images) { + function($match) use ($options, $images) { if (!($file = AttachmentFile::lookup($match[1]))) return $match[0]; + return sprintf('"%s" data-cid="%s"', - $file->getDownloadUrl(false, 'inline', $script), $match[1]); + $file->getDownloadUrl($options), $match[1]); }, $html); } diff --git a/include/class.forms.php b/include/class.forms.php index 2f8582855c64596e1f364476b4a449f0b0185d04..161e06fdb24ec34f700b025b55adff01f80f4c78 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -4513,13 +4513,15 @@ class FreeTextWidget extends Widget { if (($attachments = $this->field->getFiles()) && count($attachments)) { ?> <section class="freetext-files"> <div class="title"><?php echo __('Related Resources'); ?></div> - <?php foreach ($attachments as $attach) { ?> + <?php foreach ($attachments as $attach) { + $filename = Format::htmlchars($attach->getFilename()); + ?> <div class="file"> <a href="<?php echo $attach->file->getDownloadUrl(); ?>" - target="_blank" download="<?php echo $attach->file->getDownloadUrl(); - ?>" class="truncate no-pjax"> + target="_blank" download="<?php echo $filename; ?>" + class="truncate no-pjax"> <i class="icon-file"></i> - <?php echo Format::htmlchars($attach->getFilename()); ?> + <?php echo $filename; ?> </a> </div> <?php } ?> diff --git a/include/class.page.php b/include/class.page.php index 3ea5ca0f98839dc5fcdc4638c5de515b492bc245..a430fc2e66cd20da6b5a1711b1739094bbc8fdbc 100644 --- a/include/class.page.php +++ b/include/class.page.php @@ -70,7 +70,7 @@ class Page extends VerySimpleModel { return $this->_getLocal('body', $lang); } function getBodyWithImages() { - return Format::viewableImages($this->getLocalBody()); + return Format::viewableImages($this->getLocalBody(), ['type' => 'P']); } function _getLocal($what, $lang=false) { diff --git a/include/client/faq.inc.php b/include/client/faq.inc.php index 9bc723a6e61c4dfbdf9571ce215c8f132e22b0e7..176ae429a5065d545fb4361faee019b015232971 100644 --- a/include/client/faq.inc.php +++ b/include/client/faq.inc.php @@ -43,7 +43,8 @@ $category=$faq->getCategory(); <strong><?php echo __('Attachments');?>:</strong> <?php foreach ($attachments as $att) { ?> <div> - <a href="<?php echo $att->file->getDownloadUrl(); ?>" class="no-pjax"> + <a href="<?php echo $att->file->getDownloadUrl(['id' => $att->getId()]); + ?>" class="no-pjax"> <i class="icon-file"></i> <?php echo Format::htmlchars($att->getFilename()); ?> </a> diff --git a/include/client/templates/thread-entry.tmpl.php b/include/client/templates/thread-entry.tmpl.php index fed92a5c657b6bba249f6f365cae8462b62c895d..ad9f3dc9fa33282b4a9bec980cc7daf2ee9fc7fc 100644 --- a/include/client/templates/thread-entry.tmpl.php +++ b/include/client/templates/thread-entry.tmpl.php @@ -60,7 +60,8 @@ if ($cfg->isAvatarsEnabled() && $user) ?> <span class="attachment-info"> <i class="icon-paperclip icon-flip-horizontal"></i> - <a class="no-pjax truncate filename" href="<?php echo $A->file->getDownloadUrl(); + <a class="no-pjax truncate filename" + href="<?php echo $A->file->getDownloadUrl(['id' => $A->getId()]); ?>" download="<?php echo Format::htmlchars($A->getFilename()); ?>" target="_blank"><?php echo Format::htmlchars($A->getFilename()); ?></a><?php echo $size;?> diff --git a/include/client/view.inc.php b/include/client/view.inc.php index bc39255ab0e02367b43f151e1e1b9a05c5c41bd7..fa48bdc3eaf2bb3aaf44ca37690fa64ff509eed3 100644 --- a/include/client/view.inc.php +++ b/include/client/view.inc.php @@ -208,7 +208,7 @@ foreach (AttachmentFile::objects()->filter(array( 'attachments__inline' => true, )) as $file) { $urls[strtolower($file->getKey())] = array( - 'download_url' => $file->getDownloadUrl(), + 'download_url' => $file->getDownloadUrl(['type' => 'H']), 'filename' => $file->name, ); } ?> diff --git a/include/staff/faq-view.inc.php b/include/staff/faq-view.inc.php index a5dc4e56010c41ca059d7701d1bd19345c533661..531fbc2cd44bb603b860f5de9e4e833bcbaf2a88 100644 --- a/include/staff/faq-view.inc.php +++ b/include/staff/faq-view.inc.php @@ -41,7 +41,8 @@ if ($thisstaff->hasPerm(FAQ::PERM_MANAGE)) { ?> <?php foreach ($attachments as $att) { ?> <div> <i class="icon-paperclip pull-left"></i> - <a target="_blank" href="<?php echo $att->file->getDownloadUrl(); ?>" + <a target="_blank" href="<?php echo $att->file->getDownloadUrl(['id' => + $att->getId()]); ?>" class="attachment no-pjax"> <?php echo Format::htmlchars($att->getFilename()); ?> </a> diff --git a/include/staff/templates/thread-entries.tmpl.php b/include/staff/templates/thread-entries.tmpl.php index 0d15aa0c9f15dfd410a9c60914a5c17ef67d3ab5..9b267bbae835c20484b484db1e963a1d4e5db2a0 100644 --- a/include/staff/templates/thread-entries.tmpl.php +++ b/include/staff/templates/thread-entries.tmpl.php @@ -80,7 +80,8 @@ foreach (Attachment::objects()->filter(array( if (!$A->inline) continue; $urls[strtolower($A->file->getKey())] = array( - 'download_url' => $A->file->getDownloadUrl(), + 'download_url' => $A->file->getDownloadUrl(['id' => + $A->getId()]), 'filename' => $A->getFilename(), ); } diff --git a/include/staff/templates/thread-entry.tmpl.php b/include/staff/templates/thread-entry.tmpl.php index d36d2b7c9ab08e1e50e14829fa7726cbe5ee2050..5781db359fb9fe1383bd8f2235f536bb2f910774 100644 --- a/include/staff/templates/thread-entry.tmpl.php +++ b/include/staff/templates/thread-entry.tmpl.php @@ -101,7 +101,8 @@ if ($entry->flags & ThreadEntry::FLAG_COLLABORATOR && $entry->type == 'N') { ?> <span class="attachment-info"> <i class="icon-paperclip icon-flip-horizontal"></i> - <a class="no-pjax truncate filename" href="<?php echo $A->file->getDownloadUrl(); + <a class="no-pjax truncate filename" href="<?php echo + $A->file->getDownloadUrl(['id' => $A->getId()]); ?>" download="<?php echo Format::htmlchars($A->getFilename()); ?>" target="_blank"><?php echo Format::htmlchars($A->getFilename()); ?></a><?php echo $size;?> diff --git a/open.php b/open.php index 450b9d1f60609daaa31e77673e4898d672230403..a507bc979515181f5d46eb035a8ccaf9ade92e59 100644 --- a/open.php +++ b/open.php @@ -82,7 +82,8 @@ if ($ticket echo Format::viewableImages( $ticket->replaceVars( $page->getLocalBody() - ) + ), + ['type' => 'P'] ); } else {