From 62358bc54ed3942c00744dd70d9885e121bb312a Mon Sep 17 00:00:00 2001 From: bill bittner Date: Fri, 7 Jul 2017 16:08:35 -0700 Subject: [PATCH] extrapolated file validations --- helpers/libraries/publishHelpers.js | 47 ++++++++++++++++ public/assets/js/claimPublish.js | 42 ++++---------- public/assets/js/publishFunctions.js | 82 +++++++++++++++++++--------- routes/api-routes.js | 58 ++++---------------- views/partials/publish.handlebars | 2 +- 5 files changed, 126 insertions(+), 105 deletions(-) diff --git a/helpers/libraries/publishHelpers.js b/helpers/libraries/publishHelpers.js index c2e10091..0977d01a 100644 --- a/helpers/libraries/publishHelpers.js +++ b/helpers/libraries/publishHelpers.js @@ -3,6 +3,53 @@ const config = require('config'); const fs = require('fs'); module.exports = { + validateFile (file, name, license, nsfw) { + if (!file) { + throw new Error('No file was submitted or the key used was incorrect. Files posted through this route must use a key of "speech" or null'); + } + // check file type and size + switch (file.type) { + case 'image/jpeg': + case 'image/png': + case 'image/gif': + if (file.size > 5000000) { + throw new Error('Your image exceeds the 5 megabyte limit.'); + } + break; + case 'video/mp4': + if (file.size > 50000000) { + throw new Error('Your video exceeds the 50 megabyte limit.'); + } + break; + default: + throw new Error('The ' + file.Type + ' content type is not supported. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.'); + } + + // validate name + const invalidCharacters = /[^A-Za-z0-9,-]/.exec(name); + if (invalidCharacters) { + throw new Error('The name you provided is not allowed. Please use A-Z, a-z, 0-9, and "-" only.'); + } + // validate license + if ((license.indexOf('Public Domain') === -1) && (license.indexOf('Creative Commons') === -1)) { + throw new Error('Only posts with a license of "Public Domain" or "Creative Commons" are eligible for publishing through spee.ch'); + } + switch (nsfw) { + case true: + case false: + case 'true': + case 'false': + case 'on': + case 'off': + case 0: + case '0': + case 1: + case '1': + break; + default: + throw new Error('NSFW value was not accepted. NSFW must be set to either true, false, "on", or "off"'); + } + }, createPublishParams (name, filePath, license, nsfw) { logger.debug(`Creating Publish Parameters for "${name}"`); // const payAddress = config.get('WalletConfig.LbryPayAddress'); diff --git a/public/assets/js/claimPublish.js b/public/assets/js/claimPublish.js index ef657746..9ce7f848 100644 --- a/public/assets/js/claimPublish.js +++ b/public/assets/js/claimPublish.js @@ -4,38 +4,17 @@ var uploader = new SocketIOFileUpload(socket); var stagedFiles = null; /* configure the submit button */ -document.getElementById('publish-submit').addEventListener('click', function(event){ +function publishSelectedImage(event) { event.preventDefault(); + // validate inputs var name = document.getElementById('publish-name').value; - var invalidCharacters = /[^A-Za-z0-9,-]/.exec(name); - // validate 'name' field - if (invalidCharacters) { - alert(invalidCharacters + ' is not allowed. A-Z, a-z, 0-9, and "-" only.'); - return; - } else if (name.length < 1) { - alert("You must enter a name for your claim"); + try { + validateSubmission(stagedFiles, name); + } catch (error) { + alert(error.message); return; } - // make sure only 1 file was selected - if (!stagedFiles) { - alert("Please select a file"); - return; - } else if (stagedFiles.length > 1) { - alert("Only one file is allowed at a time"); - return; - } - // make sure the content type is acceptable - switch (stagedFiles[0].type) { - case "image/png": - case "image/jpeg": - case "image/gif": - case "video/mp4": - break; - default: - alert("Only .png, .jpeg, .gif, and .mp4 files are currently supported"); - return; - } - // make sure the name is available + // make sure the name is available then start the upload var xhttp; xhttp = new XMLHttpRequest(); xhttp.open('GET', '/api/isClaimAvailable/' + name, true); @@ -44,17 +23,18 @@ document.getElementById('publish-submit').addEventListener('click', function(eve if (this.readyState == 4 ) { if ( this.status == 200) { if (this.response == true) { - uploader.submitFiles(stagedFiles); + console.log("name is available"); + //uploader.submitFiles(stagedFiles); //note: must pass the file as part of an array. } else { alert("That name has already been claimed by spee.ch. Please choose a different name."); } } else { - console.log("request to check claim name failed with status:", this.status); + throw new Error("request to check claim name failed with status:" + this.status); }; } }; xhttp.send(); -}) +}; /* socketio-file-upload listeners */ uploader.maxFileSize = 5000000; diff --git a/public/assets/js/publishFunctions.js b/public/assets/js/publishFunctions.js index 0009496a..39377c68 100644 --- a/public/assets/js/publishFunctions.js +++ b/public/assets/js/publishFunctions.js @@ -3,8 +3,43 @@ function updatePublishStatus(msg){ document.getElementById('publish-status').innerHTML = msg; } -function resetPublishArea(){ - console.log("resetting publish area";) +function validateFile(file) { + if (!file) { + throw new Error('no file provided'); + } + // validate size and type + switch (file.type) { + case 'image/jpeg': + case 'image/png': + case 'image/gif': + if (file.size > 5000000){ + throw new Error('Sorry, images are limitted to 5 megabytes.'); + } + break; + case 'video/mp4': + if (file.size > 50000000){ + throw new Error('Sorry, videos are limitted to 50 megabytes.'); + } + break; + default: + throw new Error('The ' + file.Type + ' content type is not supported. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.') + } +} + +function validateSubmission(stagedFiles, name){ + // make sure only 1 file was selected + if (!stagedFiles) { + throw new Error("Please select a file"); + } else if (stagedFiles.length > 1) { + throw new Error("Only one file is allowed at a time"); + } + // validate 'name' field + var invalidCharacters = /[^A-Za-z0-9,-]/.exec(name); + if (invalidCharacters) { + throw new Error(invalidCharacters + ' is not allowed. A-Z, a-z, 0-9, and "-" only.'); + } else if (name.length < 1) { + throw new Error("You must enter a name for your claim"); + } } /* regular publish helper functions */ @@ -14,41 +49,34 @@ function previewAndStageFile(selectedFile){ var dropzone = document.getElementById('drop-zone'); var previewReader = new FileReader(); var nameInput = document.getElementById('publish-name'); - - preview.style.display = 'block'; - dropzone.style.display = 'none'; - // set the preview after reading the asset previewReader.onloadend = function () { + preview.style.display = 'block'; + dropzone.style.display = 'none'; if (selectedFile.type === 'video/mp4') { preview.innerHTML = ''; } else { preview.innerHTML = 'image preview'; } }; - - if (selectedFile) { - console.log(selectedFile); - if (selectedFile.size > 5000000){ - alert("Sorry, uploading is limitted to 5 megabytes."); - resetPublishArea(); - return; - } - // reads the data and sets the preview src - previewReader.readAsDataURL(selectedFile); - // set the name input value to the image name if none is set yet - if (nameInput.value === "") { - nameInput.value = selectedFile.name.substring(0, selectedFile.name.indexOf('.')); - } - // store the selected file for upload - stagedFiles = [selectedFile]; - } else { - preview.src = ''; + // validate the file + try { + validateFile(selectedFile); + } catch (error) { + alert(error.message); + return; } - + // read the data (when completed, it will trigger the asset preview) + previewReader.readAsDataURL(selectedFile); + // set the name input value to the image name if none is set yet + if (nameInput.value === "") { + nameInput.value = selectedFile.name.substring(0, selectedFile.name.indexOf('.')); + } + // store the selected file for upload + stagedFiles = [selectedFile]; } -/* drop zone function s*/ +/* drop zone functions */ function drop_handler(ev) { ev.preventDefault(); @@ -83,7 +111,7 @@ function startPublish() { //download the image var dataUrl = canvas.toDataURL('image/jpeg'); // canvas defined in memeDraw.js var blob = dataURItoBlob(dataUrl) - var fileName = nameInput.value + ".jpg"; //note: need to dynamically grab type + var fileName = nameInput.value + ".jpeg"; //note: need to dynamically grab type var file = new File([blob], fileName, {type: 'image/jpeg', lastModified: Date.now()}); stageAndPublish(file); }; diff --git a/routes/api-routes.js b/routes/api-routes.js index a0065a58..f3e06263 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -3,7 +3,7 @@ const multipart = require('connect-multiparty'); const multipartMiddleware = multipart(); const publishController = require('../controllers/publishController.js'); const lbryApi = require('../helpers/libraries/lbryApi.js'); -const publishHelpers = require('../helpers/libraries/publishHelpers.js'); +const { createPublishParams, validateFile } = require('../helpers/libraries/publishHelpers.js'); const errorHandlers = require('../helpers/libraries/errorHandlers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js'); @@ -55,64 +55,30 @@ module.exports = app => { errorHandlers.handleRequestError('publish', originalUrl, ip, error, res); }); }); + // route to run a publish request on the daemon app.post('/api/publish', multipartMiddleware, ({ body, files, headers, ip, originalUrl }, res) => { // google analytics sendGoogleAnalytics('publish', headers, ip, originalUrl); // validate that a file was provided const file = files.speech || files.null; - logger.debug(file); - if (!file) { - postToStats('publish', originalUrl, ip, 'Error: file'); - res.status(400).send('Error: No file was submitted or the key used was incorrect. Files posted through this route must use a key of "speech" or null'); - return; - } - // check if the size is 5 mb or less - if (file.size > 5000000) { - res.status(400).send('Error: only files of 5 megabytes or less are allowed'); - return; - } - // validate name const name = body.name || file.name.substring(0, file.name.indexOf('.')); - const invalidCharacters = /[^A-Za-z0-9,-]/.exec(name); - if (invalidCharacters) { - postToStats('publish', originalUrl, ip, 'Error: name'); - res.status(400).send('Error: The name you provided is not allowed. Please use A-Z, a-z, 0-9, "_" and "-" only.'); - return; - } - // validate license const license = body.license || 'No License Provided'; - if ((license.indexOf('Public Domain') === -1) && (license.indexOf('Creative Commons') === -1)) { - postToStats('puplish', originalUrl, ip, 'Error: license'); - res.status(400).send('Error: Only posts with a license of "Public Domain" or "Creative Commons" are eligible for publishing through spee.ch'); + const nsfw = body.nsfw || true; + try { + validateFile(file, name, license, nsfw); + } catch (error) { + postToStats('publish', originalUrl, ip, error.message); + logger.debug('rejected >>', error.message); + res.status(400).send(error.message); return; } - const nsfw = body.nsfw || true; - switch (nsfw) { - case true: - case false: - case 'true': - case 'false': - case 'on': - case 'off': - case 0: - case '0': - case 1: - case '1': - break; - default: - postToStats('publish', originalUrl, ip, 'Error: nsfw'); - res.status(400).send('Error: NSFW value was not accepted. NSFW must be set to either true, false, "on", or "off"'); - return; - } + + // prepare the publish parameters const fileName = file.name; const filePath = file.path; const fileType = file.type; - /* - note: make sure it's not a harmful file type - */ - // prepare the publish parameters - const publishParams = publishHelpers.createPublishParams(name, filePath, license, nsfw); + const publishParams = createPublishParams(name, filePath, license, nsfw); // publish the file publishController .publish(publishParams, fileName, fileType) diff --git a/views/partials/publish.handlebars b/views/partials/publish.handlebars index bd38e08c..d2b49224 100644 --- a/views/partials/publish.handlebars +++ b/views/partials/publish.handlebars @@ -25,7 +25,7 @@

- +

By clicking 'Publish' I attest that I have read and agree to the LBRY terms of service.