| 
									
										
										
										
											2017-02-18 01:56:23 -07:00
										 |  |  | 'use strict'; | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | const path = require('path'); | 
					
						
							|  |  |  | const nconf = require('nconf'); | 
					
						
							|  |  |  | const validator = require('validator'); | 
					
						
							|  |  |  | const winston = require('winston'); | 
					
						
							|  |  |  | const util = require('util'); | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | const db = require('../database'); | 
					
						
							|  |  |  | const meta = require('../meta'); | 
					
						
							|  |  |  | const file = require('../file'); | 
					
						
							|  |  |  | const plugins = require('../plugins'); | 
					
						
							|  |  |  | const image = require('../image'); | 
					
						
							|  |  |  | const privileges = require('../privileges'); | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | const uploadsController = module.exports; | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | uploadsController.upload = async function (req, res, filesIterator) { | 
					
						
							|  |  |  | 	let files = req.files.files; | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!Array.isArray(files)) { | 
					
						
							|  |  |  | 		return res.status(500).json('invalid files'); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (Array.isArray(files[0])) { | 
					
						
							|  |  |  | 		files = files[0]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 	// backwards compatibility
 | 
					
						
							|  |  |  | 	if (filesIterator.constructor && filesIterator.constructor.name !== 'AsyncFunction') { | 
					
						
							|  |  |  | 		winston.warn('[deprecated] uploadsController.upload, use an async function as iterator'); | 
					
						
							|  |  |  | 		filesIterator = util.promisify(filesIterator); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 	try { | 
					
						
							|  |  |  | 		const images = await Promise.all(files.map(fileObj => filesIterator(fileObj))); | 
					
						
							| 
									
										
										
										
											2017-04-14 12:59:36 -04:00
										 |  |  | 		res.status(200).json(images); | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 	} catch (err) { | 
					
						
							|  |  |  | 		res.status(500).json({ path: req.path, error: err.message }); | 
					
						
							|  |  |  | 	} finally { | 
					
						
							|  |  |  | 		deleteTempFiles(files); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | uploadsController.uploadPost = async function (req, res) { | 
					
						
							|  |  |  | 	await uploadsController.upload(req, res, async function (uploadedFile) { | 
					
						
							|  |  |  | 		const isImage = uploadedFile.type.match(/image./); | 
					
						
							| 
									
										
										
										
											2016-07-12 19:58:59 +03:00
										 |  |  | 		if (isImage) { | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 			return await uploadAsImage(req, uploadedFile); | 
					
						
							| 
									
										
										
										
											2015-11-11 13:52:29 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 		return await uploadAsFile(req, uploadedFile); | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2016-07-12 19:58:59 +03:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2016-03-23 12:19:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | async function uploadAsImage(req, uploadedFile) { | 
					
						
							|  |  |  | 	const canUpload = await privileges.global.can('upload:post:image', req.uid); | 
					
						
							|  |  |  | 	if (!canUpload) { | 
					
						
							|  |  |  | 		throw new Error('[[error:no-privileges]]'); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	await image.checkDimensions(uploadedFile.path); | 
					
						
							|  |  |  | 	await image.stripEXIF(uploadedFile.path); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (plugins.hasListeners('filter:uploadImage')) { | 
					
						
							|  |  |  | 		return await plugins.fireHook('filter:uploadImage', { | 
					
						
							|  |  |  | 			image: uploadedFile, | 
					
						
							|  |  |  | 			uid: req.uid, | 
					
						
							| 
									
										
										
										
											2020-05-22 10:00:24 -04:00
										 |  |  | 			folder: 'files', | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-29 19:53:03 -04:00
										 |  |  | 	await image.isFileTypeAllowed(uploadedFile.path); | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	let fileObj = await uploadsController.uploadFile(req.uid, uploadedFile); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (meta.config.resizeImageWidth === 0 || meta.config.resizeImageWidthThreshold === 0) { | 
					
						
							|  |  |  | 		return fileObj; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fileObj = await resizeImage(fileObj); | 
					
						
							|  |  |  | 	return { url: fileObj.url }; | 
					
						
							| 
									
										
										
										
											2016-07-12 19:58:59 +03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-04-20 13:58:25 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | async function uploadAsFile(req, uploadedFile) { | 
					
						
							|  |  |  | 	const canUpload = await privileges.global.can('upload:post:file', req.uid); | 
					
						
							|  |  |  | 	if (!canUpload) { | 
					
						
							|  |  |  | 		throw new Error('[[error:no-privileges]]'); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const fileObj = await uploadsController.uploadFile(req.uid, uploadedFile); | 
					
						
							|  |  |  | 	return { | 
					
						
							|  |  |  | 		url: fileObj.url, | 
					
						
							|  |  |  | 		name: fileObj.name, | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2016-07-12 19:58:59 +03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | async function resizeImage(fileObj) { | 
					
						
							|  |  |  | 	const imageData = await image.size(fileObj.path); | 
					
						
							|  |  |  | 	if (imageData.width < meta.config.resizeImageWidthThreshold || meta.config.resizeImageWidth > meta.config.resizeImageWidthThreshold) { | 
					
						
							|  |  |  | 		return fileObj; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	await image.resizeImage({ | 
					
						
							|  |  |  | 		path: fileObj.path, | 
					
						
							|  |  |  | 		target: file.appendToFileName(fileObj.path, '-resized'), | 
					
						
							|  |  |  | 		width: meta.config.resizeImageWidth, | 
					
						
							|  |  |  | 		quality: meta.config.resizeImageQuality, | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 	// Return the resized version to the composer/postData
 | 
					
						
							|  |  |  | 	fileObj.url = file.appendToFileName(fileObj.url, '-resized'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fileObj; | 
					
						
							| 
									
										
										
										
											2016-05-01 12:44:43 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | uploadsController.uploadThumb = async function (req, res, next) { | 
					
						
							| 
									
										
										
										
											2018-10-21 16:47:51 -04:00
										 |  |  | 	if (!meta.config.allowTopicsThumbnail) { | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 		deleteTempFiles(req.files.files); | 
					
						
							|  |  |  | 		return next(new Error('[[error:topic-thumbnails-are-disabled]]')); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 	await uploadsController.upload(req, res, async function (uploadedFile) { | 
					
						
							|  |  |  | 		if (!uploadedFile.type.match(/image./)) { | 
					
						
							|  |  |  | 			throw new Error('[[error:invalid-file]]'); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-09-29 19:53:03 -04:00
										 |  |  | 		await image.isFileTypeAllowed(uploadedFile.path); | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 		await image.resizeImage({ | 
					
						
							|  |  |  | 			path: uploadedFile.path, | 
					
						
							|  |  |  | 			width: meta.config.topicThumbSize, | 
					
						
							|  |  |  | 			height: meta.config.topicThumbSize, | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		if (plugins.hasListeners('filter:uploadImage')) { | 
					
						
							|  |  |  | 			return await plugins.fireHook('filter:uploadImage', { | 
					
						
							|  |  |  | 				image: uploadedFile, | 
					
						
							|  |  |  | 				uid: req.uid, | 
					
						
							| 
									
										
										
										
											2020-05-22 10:00:24 -04:00
										 |  |  | 				folder: 'files', | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return await uploadsController.uploadFile(req.uid, uploadedFile); | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | uploadsController.uploadFile = async function (uid, uploadedFile) { | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 	if (plugins.hasListeners('filter:uploadFile')) { | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 		return await plugins.fireHook('filter:uploadFile', { | 
					
						
							| 
									
										
										
										
											2017-02-17 19:57:18 +00:00
										 |  |  | 			file: uploadedFile, | 
					
						
							| 
									
										
										
										
											2017-02-18 19:14:39 -07:00
										 |  |  | 			uid: uid, | 
					
						
							| 
									
										
										
										
											2020-05-22 10:00:24 -04:00
										 |  |  | 			folder: 'files', | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-18 21:09:33 -05:00
										 |  |  | 	if (!uploadedFile) { | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 		throw new Error('[[error:invalid-file]]'); | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-21 16:47:51 -04:00
										 |  |  | 	if (uploadedFile.size > meta.config.maximumFileSize * 1024) { | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 		throw new Error('[[error:file-too-big, ' + meta.config.maximumFileSize + ']]'); | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-07 15:37:20 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 	const allowed = file.allowedExtensions(); | 
					
						
							| 
									
										
										
										
											2017-05-01 20:58:34 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 	const extension = path.extname(uploadedFile.name).toLowerCase(); | 
					
						
							| 
									
										
										
										
											2018-10-20 14:40:48 -04:00
										 |  |  | 	if (allowed.length > 0 && (!extension || extension === '.' || !allowed.includes(extension))) { | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 		throw new Error('[[error:invalid-file-type, ' + allowed.join(', ') + ']]'); | 
					
						
							| 
									
										
										
										
											2015-12-14 14:56:28 -05:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-22 10:00:24 -04:00
										 |  |  | 	return await saveFileToLocal(uid, 'files', uploadedFile); | 
					
						
							| 
									
										
										
										
											2017-03-02 20:51:03 +03:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2016-01-11 10:27:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-22 10:00:24 -04:00
										 |  |  | async function saveFileToLocal(uid, folder, uploadedFile) { | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 	const name = uploadedFile.name || 'upload'; | 
					
						
							|  |  |  | 	const extension = path.extname(name) || ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const filename = Date.now() + '-' + validator.escape(name.substr(0, name.length - extension.length)).substr(0, 255) + extension; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-22 10:00:24 -04:00
										 |  |  | 	const upload = await file.saveFileToLocal(filename, folder, uploadedFile.path); | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 	const storedFile = { | 
					
						
							|  |  |  | 		url: nconf.get('relative_path') + upload.url, | 
					
						
							|  |  |  | 		path: upload.path, | 
					
						
							|  |  |  | 		name: uploadedFile.name, | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	const fileKey = upload.url.replace(nconf.get('upload_url'), ''); | 
					
						
							|  |  |  | 	await db.sortedSetAdd('uid:' + uid + ':uploads', Date.now(), fileKey); | 
					
						
							|  |  |  | 	const data = await plugins.fireHook('filter:uploadStored', { uid: uid, uploadedFile: uploadedFile, storedFile: storedFile }); | 
					
						
							|  |  |  | 	return data.storedFile; | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function deleteTempFiles(files) { | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 	files.forEach(fileObj => file.delete(fileObj.path)); | 
					
						
							| 
									
										
										
										
											2015-01-10 16:40:54 -05:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-12 12:41:59 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | require('../promisify')(uploadsController, ['upload', 'uploadPost', 'uploadThumb']); |