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 = '';
}
};
-
- 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.