Просмотр исходного кода

Merge branch 'attachment-handling' of https://github.com/thororm/PrivateBin into thororm-attachment-handling

apart from resolving conflicts:
- added missing docs
- inlined functions that were used in only one location
- updated unit test to support all previews
- fixed a regression that displayed the preview even when there was no preview and too early
El RIDO 8 лет назад
Родитель
Сommit
2c82279292
19 измененных файлов с 406 добавлено и 106 удалено
  1. 1 1
      cfg/conf.sample.php
  2. 14 1
      css/bootstrap/privatebin.css
  3. 14 2
      css/privatebin.css
  4. 2 0
      i18n/de.json
  5. 2 0
      i18n/es.json
  6. 2 0
      i18n/fr.json
  7. 2 0
      i18n/it.json
  8. 2 0
      i18n/no.json
  9. 2 0
      i18n/oc.json
  10. 2 0
      i18n/pl.json
  11. 2 0
      i18n/pt.json
  12. 2 0
      i18n/ru.json
  13. 2 0
      i18n/sl.json
  14. 2 0
      i18n/zh.json
  15. 287 50
      js/privatebin.js
  16. 13 8
      js/test/AttachmentViewer.js
  17. 1 1
      lib/Configuration.php
  18. 50 40
      tpl/bootstrap.php
  19. 4 3
      tpl/page.php

+ 1 - 1
cfg/conf.sample.php

@@ -73,7 +73,7 @@ languageselection = false
 ; scripts or run your site behind certain DDoS-protection services.
 ; Check the documentation at https://content-security-policy.com/
 ; Note: If you use a bootstrap theme, you can remove the allow-popups from the sandbox restrictions.
-; cspheader = "default-src 'none'; manifest-src 'self'; connect-src *; form-action 'none'; script-src 'self'; style-src 'self'; font-src 'self'; img-src 'self' data:; referrer no-referrer; sandbox allow-same-origin allow-scripts allow-forms allow-popups"
+; cspheader = "default-src 'none'; manifest-src 'self'; connect-src *; script-src 'self'; style-src 'self'; font-src 'self'; img-src 'self' data:; media-src data:; object-src data:; Referrer-Policy: 'no-referrer'; sandbox allow-same-origin allow-scripts allow-forms allow-popups"
 
 ; stay compatible with PrivateBin Alpha 0.19, less secure
 ; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of

+ 14 - 1
css/bootstrap/privatebin.css

@@ -68,12 +68,25 @@ body.loading {
 	margin-right: 8px;
 }
 
-#image img {
+#attachmentPreview img {
 	max-width: 100%;
 	height: auto;
 	margin-bottom: 20px;
 }
 
+#attachmentPreview .pdfPreview {
+	width: 100%;
+	height: 100vh;
+	margin-bottom: 20px;
+}
+
+.dragAndDropFile{
+        color:#777;
+        font-size:1em;
+        display:inline;
+}
+
+
 #deletelink {
 	float: right;
 	margin-left: 5px;

+ 14 - 2
css/privatebin.css

@@ -104,11 +104,23 @@ h3.title {
 	font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, monospace;
 }
 
-#image img {
+#attachmentPreview img {
 	max-width: 100%;
 	height: auto;
 }
 
+#attachmentPreview .pdfPreview {
+	width: 100%;
+	height: 100vh;
+	margin-bottom: 20px;
+}
+
+.dragAndDropFile{
+        color:#777;
+        font-size:1em;
+        display:inline;
+}
+
 #status {
 	clear: both;
 	padding: 5px 10px;
@@ -249,7 +261,7 @@ input {
 	font-weight: bold !important;
 }
 
-#image, .nonworking {
+#attachmentPreview, .nonworking {
 	background-color: #fff;
 	color: #000;
 	width: 100%;

+ 2 - 0
i18n/de.json

@@ -132,6 +132,8 @@
     "Cloned: '%s'": "Geklont: '%s'",
     "The cloned file '%s' was attached to this paste.": "Die geklonte Datei '%s' wurde angehängt.",
     "Attach a file": "Datei anhängen",
+    "alternatively drag & drop a file or paste an image from the clipboard": "Alternativ Drag & Drop einer Datei oder einfügen eines Bildes aus der Zwischenablage",
+    "File too large, to display a preview. Please download the attachment.": "Datei zu groß, um als Vorschau angezeigt zu werden. Bitte Anhang herunterladen.",
     "Remove attachment": "Anhang entfernen",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "Dein Browser unterstützt das hochladen von verschlüsselten Dateien nicht. Bitte verwende einen neueren Browser.",

+ 2 - 0
i18n/es.json

@@ -132,6 +132,8 @@
     "Cloned: '%s'": "Clonado: '%s'.",
     "The cloned file '%s' was attached to this paste.": "El archivo clonado '%s' ha sido adjuntado a este texto.",
     "Attach a file": "Adjuntar archivo",
+    "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
+    "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
     "Remove attachment": "Remover adjunto",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "Tu navegador no admite la carga de archivos cifrados. Utilice un navegador más reciente.",

+ 2 - 0
i18n/fr.json

@@ -141,6 +141,8 @@
     "Cloned: '%s'": "Cloner '%s'",
     "The cloned file '%s' was attached to this paste.": "Le fichier cloné '%s' a été attaché à ce paste.",
     "Attach a file": "Attacher un fichier ",
+    "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
+    "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
     "Remove attachment": "Enlever l'attachement",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "Votre navigateur ne supporte pas l'envoi de fichiers chiffrés. Merci d'utiliser un navigateur plus récent.",

+ 2 - 0
i18n/it.json

@@ -132,6 +132,8 @@
     "Cloned: '%s'": "Clonato: '%s'",
     "The cloned file '%s' was attached to this paste.": "Il file clonato '%s' era allegato a questo messaggio.",
     "Attach a file": "Allega un file",
+    "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
+    "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
     "Remove attachment": "Rimuovi allegato",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "Il tuo browser non supporta l'invio di file cifrati. Utilizza un browser più recente.",

+ 2 - 0
i18n/no.json

@@ -132,6 +132,8 @@
     "Cloned: '%s'": "Kopiert: '%s'",
     "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
     "Attach a file": "Legg til fil",
+    "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
+    "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
     "Remove attachment": "Slett vedlegg",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "Nettleseren din støtter ikke å laste opp krypterte filer. Vennligst bruk en nyere nettleser.",

+ 2 - 0
i18n/oc.json

@@ -141,6 +141,8 @@
     "Cloned: '%s'": "Clonar: '%s'",
     "The cloned file '%s' was attached to this paste.": "Aqueste fichièr clonat '%s' es estat ajustat a aqueste tèxte.",
     "Attach a file": "Juntar un fichièr ",
+    "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
+    "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
     "Remove attachment": "Levar la pèca junta",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "Vòstre navigator es pas compatible amb lo mandadís de fichièrs chifrats. Mercés d’emplegar un navigator mai recent.",

+ 2 - 0
i18n/pl.json

@@ -132,6 +132,8 @@
     "Cloned: '%s'": "Sklonowano: '%s'",
     "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
     "Attach a file": "Załącz plik",
+    "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
+    "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
     "Remove attachment": "Usuń załącznik",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "Twoja przeglądarka nie wspiera wysyłania zaszyfrowanych plików. Użyj nowszej przeglądarki.",

+ 2 - 0
i18n/pt.json

@@ -132,6 +132,8 @@
     "Cloned: '%s'": "Clonado: '%s'",
     "The cloned file '%s' was attached to this paste.": "O arquivo clonado '%s' foi anexado a essa cópia.",
     "Attach a file": "Anexar um arquivo",
+    "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
+    "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
     "Remove attachment": "Remover anexo",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "Seu navegador não permite subir arquivos cifrados. Por favor, utilize um navegador mais recente.",

+ 2 - 0
i18n/ru.json

@@ -142,6 +142,8 @@
     "The cloned file '%s' was attached to this paste.":
         "Дубликат файла '%s' был прикреплен к этой записи.",
     "Attach a file": "Прикрепить файл",
+    "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
+    "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
     "Remove attachment": "Удалить вложение",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "Ваш браузер не поддерживает отправку зашифрованных файлов. Используйте более новый браузер.",

+ 2 - 0
i18n/sl.json

@@ -141,6 +141,8 @@
     "Cloned: '%s'": "'%s' klonirana",
     "The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
     "Attach a file": "Pripni datoteko",
+    "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
+    "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
     "Remove attachment": "Odstrani priponko",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "Tvoj brskalnik ne omogoča nalaganje zakodiranih datotek. Prosim uporabi novejši brskalnik.",

+ 2 - 0
i18n/zh.json

@@ -132,6 +132,8 @@
     "Cloned: '%s'": "克隆: '%s'",
     "The cloned file '%s' was attached to this paste.": "克隆文件 '%s' 已附加到此粘贴。",
     "Attach a file": "添加一个附件",
+    "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
+    "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
     "Remove attachment": "移除附件",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
         "您的浏览器不支持上传加密的文件,请使用更新的浏览器。",

+ 287 - 50
js/privatebin.js

@@ -1520,6 +1520,10 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
 
             // show preview
             PasteViewer.setText($message.val());
+            if (AttachmentViewer.hasAttachmentData()) {
+                var attachmentData = AttachmentViewer.getAttachmentData() || AttachmentViewer.getAttachmentLink().attr('href');
+                AttachmentViewer.handleAttachmentPreview(AttachmentViewer.getAttachmentPreview(), attachmentData);
+            }
             PasteViewer.run();
 
             // finish
@@ -1855,6 +1859,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
             $plainText.addClass('hidden');
             $prettyMessage.addClass('hidden');
             $placeholder.addClass('hidden');
+            AttachmentViewer.hideAttachmentPreview();
 
             isDisplayed = false;
         };
@@ -1907,10 +1912,13 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
     var AttachmentViewer = (function () {
         var me = {};
 
-        var $attachmentLink,
-            $attachmentPreview,
-            $attachment;
-
+        var $attachmentLink;
+        var $attachmentPreview;
+        var $attachment;
+        var attachmentData;
+        var file;
+        var $fileInput;
+        var $dragAndDropFileName;
         var attachmentHasPreview = false;
 
         /**
@@ -1923,8 +1931,6 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
          */
         me.setAttachment = function(attachmentData, fileName)
         {
-            var imagePrefix = 'data:image/';
-
             // IE does not support setting a data URI on an a element
             // Convert dataURI to a Blob and use msSaveBlob to download
             if (window.Blob && navigator.msSaveBlob) {
@@ -1960,15 +1966,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
                 $attachmentLink.attr('download', fileName);
             }
 
-            // if the attachment is an image, display it
-            if (attachmentData.substring(0, imagePrefix.length) === imagePrefix) {
-                $attachmentPreview.html(
-                    $(document.createElement('img'))
-                        .attr('src', attachmentData)
-                        .attr('class', 'img-thumbnail')
-                );
-                attachmentHasPreview = true;
-            }
+            me.handleAttachmentPreview($attachmentPreview, attachmentData);
         };
 
         /**
@@ -1989,7 +1987,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
         /**
          * removes the attachment
          *
-         * This automatically hides the attachment containers to, to
+         * This automatically hides the attachment containers too, to
          * prevent an inconsistent display.
          *
          * @name AttachmentViewer.removeAttachment
@@ -1997,12 +1995,18 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
          */
         me.removeAttachment = function()
         {
+            if (!$attachment.length) {
+                return;
+            }
             me.hideAttachment();
             me.hideAttachmentPreview();
-            $attachmentLink.prop('href', '');
-            $attachmentLink.prop('download', '');
+            $attachmentLink.removeAttr('href');
+            $attachmentLink.removeAttr('download');
             $attachmentLink.off('click');
             $attachmentPreview.html('');
+
+            file = undefined;
+            attachmentData = undefined;
         };
 
         /**
@@ -2028,7 +2032,9 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
          */
         me.hideAttachmentPreview = function()
         {
-            $attachmentPreview.addClass('hidden');
+            if ($attachmentPreview) {
+                $attachmentPreview.addClass('hidden');
+            }
         };
 
         /**
@@ -2039,8 +2045,25 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
          */
         me.hasAttachment = function()
         {
+            if (!$attachment.length) {
+                return false;
+            }
             var link = $attachmentLink.prop('href');
-            return typeof link !== 'undefined' && link !== '';
+            return (typeof link !== 'undefined' && link !== '');
+        };
+
+        /**
+         * checks if there is attachment data available
+         *
+         * @name   AttachmentViewer.hasAttachmentData
+         * @function
+         */
+        me.hasAttachmentData = function()
+        {
+            if ($attachment.length) {
+                return true;
+            }
+            return false;
         };
 
         /**
@@ -2077,6 +2100,224 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
             I18n._($attachmentLink, label, $attachmentLink.attr('download'));
         };
 
+        /**
+         * read file data as dataURL using the FileReader API
+         *
+         * @name   AttachmentViewer.readFileData
+         * @function
+         * @param {object} loadedFile The loaded file.
+         * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL()}
+         */
+        me.readFileData = function (loadedFile) {
+            if (typeof FileReader === 'undefined') {
+                // revert loading status…
+                me.hideAttachment();
+                me.hideAttachmentPreview();
+                Alert.showError('Your browser does not support uploading encrypted files. Please use a newer browser.');
+                return;
+            }
+
+            var fileReader = new FileReader();
+            if (loadedFile === undefined) {
+                loadedFile = $fileInput[0].files[0];
+                $dragAndDropFileName.text('');
+            } else {
+                $dragAndDropFileName.text(loadedFile.name);
+            }
+
+            file = loadedFile;
+
+            fileReader.onload = function (event) {
+                var dataURL = event.target.result;
+                attachmentData = dataURL;
+
+                if (Editor.isPreview()) {
+                    me.handleAttachmentPreview($attachmentPreview, dataURL);
+                    $attachmentPreview.removeClass('hidden');
+                }
+            };
+            fileReader.readAsDataURL(loadedFile);
+        };
+
+        /**
+         * handle the preview of files that can either be an image, video, audio or pdf element
+         *
+         * @name   AttachmentViewer.handleAttachmentPreview
+         * @function
+         * @argument {jQuery} $targetElement where the preview should be appended.
+         * @argument {File Data} data of the file to be displayed.
+         */
+        me.handleAttachmentPreview = function ($targetElement, data) {
+            if (data) {
+                // source: https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL()
+                var mimeType = data.slice(
+                    data.indexOf('data:') + 5,
+                    data.indexOf(';base64,')
+                );
+
+                attachmentHasPreview = true;
+                if (mimeType.match(/image\//i)) {
+                    $targetElement.html(
+                        $(document.createElement('img'))
+                            .attr('src', data)
+                            .attr('class', 'img-thumbnail')
+                    );
+                } else if (mimeType.match(/video\//i)) {
+                    $targetElement.html(
+                        $(document.createElement('video'))
+                            .attr('controls', 'true')
+                            .attr('autoplay', 'true')
+                            .attr('class', 'img-thumbnail')
+
+                            .append($(document.createElement('source'))
+                            .attr('type', mimeType)
+                            .attr('src', data))
+                    );
+                } else if (mimeType.match(/audio\//i)) {
+                    $targetElement.html(
+                        $(document.createElement('audio'))
+                            .attr('controls', 'true')
+                            .attr('autoplay', 'true')
+
+                            .append($(document.createElement('source'))
+                            .attr('type', mimeType)
+                            .attr('src', data))
+                    );
+                } else if (mimeType.match(/\/pdf/i)) {
+                    // PDFs are only displayed if the filesize is smaller than about 1MB (after base64 encoding).
+                    // Bigger filesizes currently cause crashes in various browsers.
+                    // See also: https://code.google.com/p/chromium/issues/detail?id=69227
+
+                    // Firefox crashes with files that are about 1.5MB
+                    // The performance with 1MB files is bearable
+                    if (data.length > 1398488) {
+                        Alert.showError('File too large, to display a preview. Please download the attachment.');
+                        return;
+                    }
+
+                    // Fallback for browsers, that don't support the vh unit
+                    var clientHeight = $(window).height();
+
+                    $targetElement.html(
+                        $(document.createElement('embed'))
+                            .attr('src', data)
+                            .attr('type', 'application/pdf')
+                            .attr('class', 'pdfPreview')
+                            .css('height', clientHeight)
+                    );
+                } else {
+                    attachmentHasPreview = false;
+                }
+            }
+        };
+
+        /**
+         * attaches the file attachment drag & drop handler to the page
+         *
+         * @name   AttachmentViewer.addDragDropHandler
+         * @function
+         */
+        me.addDragDropHandler = function () {
+            if (typeof $fileInput === 'undefined' || $fileInput.length === 0) {
+                return;
+            }
+
+            var ignoreDragDrop = function(event) {
+                event.stopPropagation();
+                event.preventDefault();
+            };
+
+            var drop = function(event) {
+                var evt = event.originalEvent;
+                evt.stopPropagation();
+                evt.preventDefault();
+
+                if ($fileInput) {
+                    var file = evt.dataTransfer.files[0];
+                    //Clear the file input:
+                    $fileInput.wrap('<form>').closest('form').get(0).reset();
+                    $fileInput.unwrap();
+                    //Only works in Chrome:
+                    //fileInput[0].files = e.dataTransfer.files;
+
+                    me.readFileData(file);
+                }
+            };
+
+            $(document).on('drop', drop);
+            $(document).on('dragenter', ignoreDragDrop);
+            $(document).on('dragover', ignoreDragDrop);
+            $fileInput.on("change", function () {
+                me.readFileData();
+            });
+        };
+
+        /**
+         * attaches the clipboard attachment handler to the page
+         *
+         * @name   AttachmentViewer.addClipboardEventHandler
+         * @function
+         */
+        me.addClipboardEventHandler = function () {
+            $(document).on('paste',
+                    function (event) {
+                        var items = (event.clipboardData || event.originalEvent.clipboardData).items;
+                        for (var i in items) {
+                            if (items.hasOwnProperty(i)) {
+                                var item = items[i];
+                                if (item.kind === 'file') {
+                                    me.readFileData(item.getAsFile());
+                                }
+                            }
+                        }
+                    });
+        };
+
+
+        /**
+         * getter for attachment data
+         *
+         * @name   AttachmentViewer.getAttachmentData
+         * @function
+         * @return {jQuery}
+         */
+        me.getAttachmentData = function () {
+            return attachmentData;
+        };
+
+        /**
+         * getter for attachment link
+         *
+         * @name   AttachmentViewer.getAttachmentLink
+         * @function
+         * @return {jQuery}
+         */
+        me.getAttachmentLink = function () {
+            return $attachmentLink;
+        };
+
+        /**
+         * getter for attachment preview
+         *
+         * @name   AttachmentViewer.getAttachmentPreview
+         * @function
+         * @return {jQuery}
+         */
+        me.getAttachmentPreview = function () {
+            return $attachmentPreview;
+        };
+
+        /**
+         * getter for file data, returns the file contents
+         *
+         * @name   AttachmentViewer.getFile
+         * @function
+         * @return {string}
+         */
+        me.getFile = function () {
+            return file;
+        };
+
         /**
          * initiate
          *
@@ -2088,10 +2329,16 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
         me.init = function()
         {
             $attachment = $('#attachment');
-            $attachmentLink = $('#attachment a');
-            $attachmentPreview = $('#attachmentPreview');
-            attachmentHasPreview = false;
-        };
+            if($attachment.length){
+                $attachmentLink = $('#attachment a');
+                $attachmentPreview = $('#attachmentPreview');
+                $dragAndDropFileName = $('#dragAndDropFileName');
+
+                $fileInput = $('#file');
+                me.addDragDropHandler();
+                me.addClipboardEventHandler();
+            }
+        }
 
         return me;
     })();
@@ -3328,31 +3575,19 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
          * @name PasteEncrypter.encryptAttachments
          * @private
          * @function
-         * @param {File|null|undefined} file - optional, falls back to cloned attachment
          * @param {function} callback - excuted when action is successful
          */
-        function encryptAttachments(file, callback) {
-            if (typeof file !== 'undefined' && file !== null) {
-                // check file reader requirements for upload
-                if (typeof FileReader === 'undefined') {
-                    Alert.showError('Your browser does not support uploading encrypted files. Please use a newer browser.');
-                    // cancels process as it does not execute callback
-                    return;
-                }
+        function encryptAttachments(callback) {
+            var file = AttachmentViewer.getAttachmentData();
 
-                var reader = new FileReader();
-
-                // closure to capture the file information
-                reader.onload = function(event) {
-                    Uploader.setData('attachment', event.target.result);
-                    Uploader.setData('attachmentname', file.name);
+            if (typeof file !== 'undefined' && file !== null) {
+                var fileName = AttachmentViewer.getFile().name;
 
-                    // run callback
-                    return callback();
-                };
+                Uploader.setData('attachment', file);
+                Uploader.setData('attachmentname', fileName);
 
-                // actually read first file
-                reader.readAsDataURL(file);
+                // run callback
+                return callback();
             } else if (AttachmentViewer.hasAttachment()) {
                 // fall back to cloned part
                 var attachment = AttachmentViewer.getAttachment();
@@ -3461,7 +3696,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
             // get data
             var plainText = Editor.getText(),
                 format = PasteViewer.getFormat(),
-                files = TopNav.getFileList();
+                files = TopNav.getFileList() || AttachmentViewer.getFile() || AttachmentViewer.hasAttachment();
 
             // do not send if there is no data
             if (plainText.length === 0 && files === null) {
@@ -3512,7 +3747,6 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
 
             // encrypt attachments
             encryptAttachments(
-                files === null ? null : files[0],
                 function () {
                     // send data
                     Uploader.run();
@@ -3727,10 +3961,12 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
             try {
                 // decrypt attachments
                 if (paste.attachment) {
-                    // try to decrypt paste and if it fails (because the password is
-                    // missing) return to let JS continue and wait for user
-                    if (!decryptAttachment(paste, key, password)) {
-                        return;
+                    if (AttachmentViewer.hasAttachmentData()) {
+                        // try to decrypt paste and if it fails (because the password is
+                        // missing) return to let JS continue and wait for user
+                        if (!decryptAttachment(paste, key, password)) {
+                            return;
+                        }
                     }
                     // ignore empty paste, as this is allowed when pasting attachments
                     decryptPaste(paste, key, password, true);
@@ -3813,6 +4049,7 @@ jQuery.PrivateBin = (function($, sjcl, Base64, RawDeflate) {
             Editor.resetInput();
             Editor.show();
             Editor.focusInput();
+            AttachmentViewer.removeAttachment();
 
             TopNav.showCreateButtons();
             Alert.hideLoading();

+ 13 - 8
js/test/AttachmentViewer.js

@@ -18,7 +18,12 @@ describe('AttachmentViewer', function () {
             function (mimeType, base64, filename, prefix, postfix) {
                 var clean = jsdom(),
                     data = 'data:' + mimeType + ';base64,' + base64.join(''),
-                    isImage = mimeType.substring(0, 6) === 'image/',
+                    previewSupported = (
+                        mimeType.substring(0, 6) === 'image/' ||
+                        mimeType.substring(0, 6) === 'audio/' ||
+                        mimeType.substring(0, 6) === 'video/' ||
+                        mimeType.match(/\/pdf/i)
+                    ),
                     results = [];
                 prefix = prefix.replace(/%(s|d)/g, '%%');
                 postfix = postfix.replace(/%(s|d)/g, '%%');
@@ -40,32 +45,32 @@ describe('AttachmentViewer', function () {
                 } else {
                     $.PrivateBin.AttachmentViewer.setAttachment(data);
                 }
-                var attachement = $.PrivateBin.AttachmentViewer.getAttachment();
+                var attachment = $.PrivateBin.AttachmentViewer.getAttachment();
                 results.push(
                     $.PrivateBin.AttachmentViewer.hasAttachment() &&
                     $('#attachment').hasClass('hidden') &&
                     $('#attachmentPreview').hasClass('hidden') &&
-                    attachement[0] === data &&
-                    attachement[1] === filename
+                    attachment[0] === data &&
+                    attachment[1] === filename
                 );
                 $.PrivateBin.AttachmentViewer.showAttachment();
                 results.push(
                     !$('#attachment').hasClass('hidden') &&
-                    (isImage ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
+                    (previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
                 );
                 $.PrivateBin.AttachmentViewer.hideAttachment();
                 results.push(
                     $('#attachment').hasClass('hidden') &&
-                    (isImage ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
+                    (previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
                 );
-                if (isImage) {
+                if (previewSupported) {
                     $.PrivateBin.AttachmentViewer.hideAttachmentPreview();
                     results.push($('#attachmentPreview').hasClass('hidden'));
                 }
                 $.PrivateBin.AttachmentViewer.showAttachment();
                 results.push(
                     !$('#attachment').hasClass('hidden') &&
-                    (isImage ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
+                    (previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
                 );
                 var element = $('<div></div>');
                 $.PrivateBin.AttachmentViewer.moveAttachmentTo(element, prefix + '%s' + postfix);

+ 1 - 1
lib/Configuration.php

@@ -54,7 +54,7 @@ class Configuration
             'urlshortener'             => '',
             'qrcode'                   => true,
             'icon'                     => 'identicon',
-            'cspheader'                => 'default-src \'none\'; manifest-src \'self\'; connect-src *; form-action \'none\'; script-src \'self\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data:; referrer no-referrer; sandbox allow-same-origin allow-scripts allow-forms allow-popups',
+            'cspheader'                => 'default-src \'none\'; manifest-src \'self\'; connect-src *; script-src \'self\'; style-src \'self\'; font-src \'self\'; img-src \'self\' data:; media-src data:; object-src data:; Referrer-Policy: \'no-referrer\'; sandbox allow-same-origin allow-scripts allow-forms allow-popups',
             'zerobincompatibility'     => false,
         ),
         'expire' => array(

+ 50 - 40
tpl/bootstrap.php

@@ -75,7 +75,7 @@ if ($MARKDOWN):
 <?php
 endif;
 ?>
-		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-fJAdh2DQpns2TMhDmiohfNCQXpjX+f/KpRCiRnWI6hq0lOT/RRFc11+qJzGK5zNFjDvDhCMbMADKn4D03p9Uag==" crossorigin="anonymous"></script>
+		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-5MEpLVeCKou7sL5U7X8uo5O0CG+1+aLGpN2O5Xvo9e31Ql7/7N8X03OCcwqBNEKv61IcSlxkCP9eLXZl8eMeBA==" crossorigin="anonymous"></script>
 		<!--[if lt IE 10]>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
 		<![endif]-->
@@ -323,8 +323,9 @@ if ($FILEUPLOAD):
 						<ul class="dropdown-menu">
 							<li id="filewrap">
 								<div>
-									<input type="file" id="file" name="file" />
+									<input type="file" id="file" name="file"/>
 								</div>
+								<div id="dragAndDropFileName" class="dragAndDropFile"><?php echo I18n::_('alternatively drag & drop a file or paste an image from the clipboard'); ?></div>
 							</li>
 							<li id="customattachment" class="hidden"></li>
 							<li>
@@ -420,30 +421,30 @@ endif;
 <?php
 if (strlen($NOTICE)):
 ?>
-				<div role="alert" class="alert alert-info">
+			<div role="alert" class="alert alert-info">
 					<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
 					<?php echo htmlspecialchars($NOTICE), PHP_EOL; ?>
-				</div>
+			</div>
 <?php
 endif;
 ?>
-				<div id="remainingtime" role="alert" class="hidden alert alert-info">
+			<div id="remainingtime" role="alert" class="hidden alert alert-info">
 					<span class="glyphicon glyphicon-fire" aria-hidden="true"></span>
-				</div>
+			</div>
 <?php
 if ($FILEUPLOAD):
 ?>
-				<div id="attachment" role="alert" class="hidden alert alert-info">
+			<div id="attachment" role="alert" class="hidden alert alert-info">
 					<span class="glyphicon glyphicon-download-alt" aria-hidden="true"></span>
 					<a class="alert-link"><?php echo I18n::_('Download attachment'), PHP_EOL; ?></a>
-				</div>
+			</div>
 <?php
 endif;
 ?>
 				<div id="status" role="alert" class="statusmessage alert alert-info<?php echo empty($STATUS) ? ' hidden' : '' ?>">
 					<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
 					<?php echo htmlspecialchars($STATUS), PHP_EOL; ?>
-				</div>
+			</div>
 				<div id="errormessage" role="alert" class="statusmessage<?php echo empty($ERROR) ? ' hidden' : '' ?> alert alert-danger">
 					<span class="glyphicon glyphicon-alert" aria-hidden="true"></span>
 					<?php echo htmlspecialchars($ERROR), PHP_EOL; ?>
@@ -461,14 +462,22 @@ endif;
 				<div id="ienotice" role="alert" class="hidden alert alert-<?php echo $isDark ? 'error' : 'warning'; ?>">
 					<span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span>
 					<?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
-					<a href="https://www.mozilla.org/firefox/">Firefox</a>,
-					<a href="https://www.opera.com/">Opera</a>,
+				<a href="https://www.mozilla.org/firefox/">Firefox</a>,
+				<a href="https://www.opera.com/">Opera</a>,
 					<a href="https://www.google.com/chrome">Chrome</a>…
+<<<<<<< HEAD
 				</div>
 				<div id="pastesuccess" role="alert" class="hidden alert alert-success">
 					<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
 					<div id="deletelink"></div>
 					<div id="pastelink"></div>
+=======
+			</div>
+				<div id="pasteSuccess" role="alert" class="hidden alert alert-success">
+				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
+				<div id="deletelink"></div>
+				<div id="pastelink">
+>>>>>>> 08972e4da33c72a2fad7d04ce065147f5fa5f0c1
 <?php
 if (strlen($URLSHORTENER)):
 ?>
@@ -479,44 +488,45 @@ if (strlen($URLSHORTENER)):
 endif;
 ?>
 				</div>
+			</div>
 				<ul id="editorTabs" class="nav nav-tabs hidden">
-					<li role="presentation" class="active"><a id="messageedit" href="#"><?php echo I18n::_('Editor'); ?></a></li>
-					<li role="presentation"><a id="messagepreview" href="#"><?php echo I18n::_('Preview'); ?></a></li>
-				</ul>
+				<li role="presentation" class="active"><a id="messageedit" href="#"><?php echo I18n::_('Editor'); ?></a></li>
+				<li role="presentation"><a id="messagepreview" href="#"><?php echo I18n::_('Preview'); ?></a></li>
+			</ul>
 			</section>
-			<section class="container">
-				<article class="row">
+		<section class="container">
+			<article class="row">
 					<div id="placeholder" class="col-md-12 hidden"><?php echo I18n::_('+++ no paste text +++'); ?></div>
 					<div id="attachmentPreview" class="col-md-12 text-center hidden"></div>
-					<div id="prettymessage" class="col-md-12 hidden">
-						<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
-					</div>
+				<div id="prettymessage" class="col-md-12 hidden">
+					<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
+				</div>
 					<div id="plaintext" class="col-md-12 hidden"></div>
-					<p class="col-md-12"><textarea id="message" name="message" cols="80" rows="25" class="form-control hidden"></textarea></p>
-				</article>
-			</section>
-			<section class="container">
-				<div id="discussion" class="hidden">
-					<h4><?php echo I18n::_('Discussion'); ?></h4>
+				<p class="col-md-12"><textarea id="message" name="message" cols="80" rows="25" class="form-control hidden"></textarea></p>
+			</article>
+		</section>
+		<section class="container">
+			<div id="discussion" class="hidden">
+				<h4><?php echo I18n::_('Discussion'); ?></h4>
 					<div id="commentcontainer"></div>
-				</div>
-			</section>
-			<section class="container">
+			</div>
+		</section>
+		<section class="container">
 				<div id="noscript" role="alert" class="nonworking alert alert-info noscript-hide">
 					<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
 					<?php echo I18n::_('Loading…'); ?><br />
-					<span class="small"><?php echo I18n::_('In case this message never disappears please have a look at <a href="https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away">this FAQ for information to troubleshoot</a>.'); ?></span>
-				</div>
-			</section>
-			<footer class="container">
-				<div class="row">
-					<h4 class="col-md-5 col-xs-8"><?php echo I18n::_($NAME); ?> <small>- <?php echo I18n::_('Because ignorance is bliss'); ?></small></h4>
-					<p class="col-md-1 col-xs-4 text-center"><?php echo $VERSION; ?></p>
-					<p id="aboutbox" class="col-md-6 col-xs-12">
-						<?php echo I18n::_('%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://privatebin.info/">project page</a>.', I18n::_($NAME)), PHP_EOL; ?>
-					</p>
-				</div>
-			</footer>
+				<span class="small"><?php echo I18n::_('In case this message never disappears please have a look at <a href="https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away">this FAQ for information to troubleshoot</a>.'); ?></span>
+			</div>
+		</section>
+		<footer class="container">
+			<div class="row">
+				<h4 class="col-md-5 col-xs-8"><?php echo I18n::_($NAME); ?> <small>- <?php echo I18n::_('Because ignorance is bliss'); ?></small></h4>
+				<p class="col-md-1 col-xs-4 text-center"><?php echo $VERSION; ?></p>
+				<p id="aboutbox" class="col-md-6 col-xs-12">
+					<?php echo I18n::_('%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href="https://privatebin.info/">project page</a>.', I18n::_($NAME)), PHP_EOL; ?>
+				</p>
+			</div>
+		</footer>
 		</main>
 		<div id="serverdata" class="hidden" aria-hidden="true">
 			<div id="cipherdata"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>

+ 4 - 3
tpl/page.php

@@ -54,7 +54,7 @@ if ($QRCODE):
 <?php
 endif;
 ?>
-		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-fJAdh2DQpns2TMhDmiohfNCQXpjX+f/KpRCiRnWI6hq0lOT/RRFc11+qJzGK5zNFjDvDhCMbMADKn4D03p9Uag==" crossorigin="anonymous"></script>
+		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-5MEpLVeCKou7sL5U7X8uo5O0CG+1+aLGpN2O5Xvo9e31Ql7/7N8X03OCcwqBNEKv61IcSlxkCP9eLXZl8eMeBA==" crossorigin="anonymous"></script>
 		<!--[if lt IE 10]>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
 		<![endif]-->
@@ -222,6 +222,7 @@ if ($FILEUPLOAD):
 				<div id="attach" class="hidden">
 					<span id="clonedfile" class="hidden"><?php echo I18n::_('Cloned file attached.'); ?></span>
 					<span id="filewrap"><?php echo I18n::_('Attach a file'); ?>: <input type="file" id="file" name="file" /></span>
+					<span id="dragAndDropFileName" class="dragAndDropFile"><?php echo I18n::_('alternatively drag & drop a file or paste an image from the clipboard'); ?></span>
 					<button id="fileremovebutton"><?php echo I18n::_('Remove attachment'); ?></button>
 				</div>
 <?php
@@ -231,7 +232,7 @@ endif;
 					<button id="messageedit"><?php echo I18n::_('Editor'); ?></button>
 					<button id="messagepreview"><?php echo I18n::_('Preview'); ?></button>
 				</div>
-				<div id="image" class="hidden"></div>
+				<div id="attachmentPreview" class="hidden"></div>
 				<div id="prettymessage" class="hidden">
 					<pre id="prettyprint" class="prettyprint linenums:1"></pre>
 				</div>
@@ -246,7 +247,7 @@ endif;
 			</div>
 		</section>
 		<div id="serverdata" class="hidden" aria-hidden="true">
-			<div id="cipherdata" class="hidden"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
+		<div id="cipherdata" class="hidden"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
 <?php
 if ($DISCUSSION):
 ?>