diff --git a/include/class.attachment.php b/include/class.attachment.php index 09d9826fd0444748830e63b18e5caf9bb5441f54..937d09edd5346d331170752077e1b00f9a354e51 100644 --- a/include/class.attachment.php +++ b/include/class.attachment.php @@ -175,6 +175,7 @@ class GenericAttachments { foreach ($this->attachments as $a) { if ($a['inline'] != $separate || $a['inline'] == $inlines) { $a['file_id'] = $a['id']; + $a['hash'] = md5($a['file_id'].session_id().strtolower($a['key'])); $attachments[] = $a; } } diff --git a/include/class.i18n.php b/include/class.i18n.php index b7e7289430bf993618579a5b2f5726d7c682e100..59c2061ee6c5e789f6bcc6be04ff7becae332276 100644 --- a/include/class.i18n.php +++ b/include/class.i18n.php @@ -158,7 +158,7 @@ class Internationalization { // Consider all subdirectories and .phar files in the base dir $dirs = glob(I18N_DIR . '*', GLOB_ONLYDIR | GLOB_NOSORT); - $phars = glob(I18N_DIR . '*.phar', GLOB_NOSORT); + $phars = glob(I18N_DIR . '*.phar', GLOB_NOSORT) ?: array(); $installed = array(); foreach (array_merge($dirs, $phars) as $f) { diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php index f1ea77d74ac9979bdffccfd195ca76eabd98c3a9..dedfba84c028c19a8f1bbe62a091e0ab337bffff 100644 --- a/include/class.mailfetch.php +++ b/include/class.mailfetch.php @@ -541,7 +541,8 @@ class MailFetcher { $body = new TextThreadBody( Format::html2text(Format::safe_html($html), 100, false)); - else + + if (!isset($body)) $body = new TextThreadBody(''); if ($cfg->stripQuotedReply()) diff --git a/include/class.mailparse.php b/include/class.mailparse.php index 0ec0b4f190217b90c633f220f6440a7c89e625ce..654e0e05ed66d7a8bca9af76d3e9e3db9ba601dc 100644 --- a/include/class.mailparse.php +++ b/include/class.mailparse.php @@ -292,7 +292,8 @@ class Mail_Parse { $body = new TextThreadBody( Format::html2text(Format::safe_html($html), 100, false)); - else + + if (!isset($body)) $body = new TextThreadBody(''); if ($cfg && $cfg->stripQuotedReply()) diff --git a/include/class.osticket.php b/include/class.osticket.php index 98979448ccf100f271caa41670046301ebf55e63..abd563839d6be9910ae4446f4aaf71694b1dc96b 100644 --- a/include/class.osticket.php +++ b/include/class.osticket.php @@ -144,7 +144,7 @@ class osTicket { $allowed = array_map('trim', explode(',', strtolower($allowedFileTypes))); $filename = is_array($file)?$file['name']:$file; - $ext = strtolower(preg_replace("/.*\.(.{3,4})$/", "$1", $filename)); + $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); //TODO: Check MIME type - file ext. shouldn't be solely trusted. diff --git a/include/class.thread.php b/include/class.thread.php index 0722c80d2027ca5691afe4dc37e805911f86658b..fbe50bc87cdfaf94447b1167837041d65aa53574 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -973,12 +973,23 @@ Class ThreadEntry { foreach ($vars['attachments'] as $i=>$a) { if (@$a['cid'] && $a['cid'] == $cid) { // Inline referenced attachment was stripped - unset($vars['attachments']); + unset($vars['attachments'][$i]); } } } } + // Handle extracted embedded images (<img src="data:base64,..." />). + // The extraction has already been performed in the ThreadBody + // class. Here they should simply be added to the attachments list + if ($atts = $vars['body']->getEmbeddedHtmlImages()) { + if (!is_array($vars['attachments'])) + $vars['attachments'] = array(); + foreach ($atts as $info) { + $vars['attachments'][] = $info; + } + } + if (!($body = Format::sanitize( (string) $vars['body']->convertTo('html')))) $body = '-'; //Special tag used to signify empty message as stored. @@ -1230,6 +1241,7 @@ class ThreadBody /* extends SplString */ { var $body; var $type; var $stripped_images = array(); + var $embedded_images = array(); function __construct($body, $type='text') { $type = strtolower($type); @@ -1282,6 +1294,10 @@ class ThreadBody /* extends SplString */ { return $this->stripped_images; } + function getEmbeddedHtmlImages() { + return $this->embedded_images; + } + function __toString() { return $this->body; } @@ -1294,8 +1310,22 @@ class TextThreadBody extends ThreadBody { } class HtmlThreadBody extends ThreadBody { function __construct($body) { + $body = $this->extractEmbeddedHtmlImages($body); $body = trim($body, " <>br/\t\n\r") ? Format::safe_html($body) : ''; parent::__construct($body, 'html'); } + + function extractEmbeddedHtmlImages($body) { + $self = $this; + return preg_replace_callback('/src="(data:[^"]+)"/', + function ($m) use ($self) { + $info = Format::parseRfc2397($m[1], false, false); + $info['cid'] = 'img'.Misc::randCode(12); + list(,$type) = explode('/', $info['type'], 2); + $info['name'] = 'image'.Misc::randCode(4).'.'.$type; + $self->embedded_images[] = $info; + return 'src="cid:'.$info['cid'].'"'; + }, $body); + } } ?> diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js index d41c87ba68370adcbf36ae21f7a167025d72d0a7..4a919612bce3612a623f62a60085f04e883a5018 100644 --- a/js/redactor-osticket.js +++ b/js/redactor-osticket.js @@ -87,6 +87,10 @@ RedactorPlugins.draft = { self.opts.imageUpload = 'ajax.php/draft/'+data.draft_id+'/attach'; self.opts.imageUploadErrorCallback = self.displayError; + // XXX: This happens in ::buildBindKeyboard() from + // ::buildAfter(). However, the imageUpload option is not + // known when the Redactor is init()'d + self.$editor.on('drop.redactor', $.proxy(self.buildEventDrop, self)); } }); this.opts.original_autosave = this.opts.autosave; diff --git a/kb/file.php b/kb/file.php index 21336765817fa588a82a983af5e8b52dc8da2a85..b06b256a35a6ebdd9c40137f9fec0b6a2f797343 100644 --- a/kb/file.php +++ b/kb/file.php @@ -23,7 +23,7 @@ $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(substr($h,-32),md5($file->getId().session_id().$file->getKey()))) //next 32 is file id + session hash. + || strcasecmp($h, $file->getDownloadHash())) //next 32 is file id + session hash. die('Unknown or invalid file. #'.Format::htmlchars($_GET['h'])); $file->download(); diff --git a/scp/file.php b/scp/file.php index 9d6518d0ae4f4d53656389503bb83c71682a8963..68197cc566cf05f707d7d0d458b7097422b7b8ae 100644 --- a/scp/file.php +++ b/scp/file.php @@ -23,7 +23,7 @@ $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. - || $file->getDownloadHash() != $h) //next 32 is file id + session hash. + || strcasecmp($file->getDownloadHash(), $h)) //next 32 is file id + session hash. die('Unknown or invalid file. #'.Format::htmlchars($_GET['h'])); $file->download(); diff --git a/scp/js/scp.js b/scp/js/scp.js index 2119131553d6c7f48d4601be8f8a529f2d7f3114..79f5804f2c5727ecf13694b4f4f5112020aa2987 100644 --- a/scp/js/scp.js +++ b/scp/js/scp.js @@ -231,7 +231,7 @@ $(document).ready(function(){ if(!$('.canned_attachments #f'+j.id,fObj).length) { var file='<span><label><input type="checkbox" name="cannedattachments[]" value="' + j.id+'" id="f'+j.id+'" checked="checked">'; file+= ' '+ j.name + '</label>'; - file+= ' (<a href="file.php?h=' + j.hash + j.key+ '">view</a>) </span>'; + file+= ' (<a href="file.php?h=' + j.key + j.hash + '">view</a>) </span>'; $('.canned_attachments', fObj).append(file); } diff --git a/setup/test/tests/test.validation.php b/setup/test/tests/test.validation.php index 6323cd1590856e82ef091e7cce983558fd28c91c..27e61af8438a4fc8a75bcb3003013af6ab56801d 100644 --- a/setup/test/tests/test.validation.php +++ b/setup/test/tests/test.validation.php @@ -32,6 +32,7 @@ class TestValidation extends Test { $this->assert(Validator::is_email('jared.12@domain.tld')); $this->assert(Validator::is_email('jared_12@domain.tld')); $this->assert(Validator::is_email('jared-12@domain.tld')); + $this->assert(Validator::is_email('jared+ost@domain.tld')); // Illegal or unsupported $this->assert(!Validator::is_email('jared r@domain.tld'));