import { EmojiCustom } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { WebApp } from 'meteor/webapp'; import _ from 'underscore'; import { settings } from '../../../../settings'; import { SystemLogger } from '../../../logger/system'; import { RocketChatFile } from '../../file'; export let RocketChatFileEmojiCustomInstance; const writeSvgFallback = (res, req) => { res.setHeader('Content-Type', 'image/svg+xml'); res.setHeader('Cache-Control', 'public, max-age=0'); res.setHeader('Expires', '-1'); res.setHeader('Last-Modified', 'Thu, 01 Jan 2015 00:00:00 GMT'); const reqModifiedHeader = req.headers['if-modified-since']; if (reqModifiedHeader != null) { if (reqModifiedHeader === 'Thu, 01 Jan 2015 00:00:00 GMT') { res.writeHead(304); res.end(); return; } } const color = '#000'; const initials = '?'; const svg = ` ${initials} `; res.write(svg); res.end(); }; const initializeEmojiCustomStorage = () => { let storeType = 'GridFS'; if (settings.get('EmojiUpload_Storage_Type')) { storeType = settings.get('EmojiUpload_Storage_Type'); } const RocketChatStore = RocketChatFile[storeType]; if (RocketChatStore == null) { throw new Error(`Invalid RocketChatStore type [${storeType}]`); } SystemLogger.info({ msg: 'Using custom emoji storage', storeType, }); let path = '~/uploads'; if (settings.get('EmojiUpload_FileSystemPath') != null) { if (settings.get('EmojiUpload_FileSystemPath').trim() !== '') { path = settings.get('EmojiUpload_FileSystemPath'); } } RocketChatFileEmojiCustomInstance = new RocketChatStore({ name: 'custom_emoji', absolutePath: path, }); }; Meteor.startup(() => { initializeEmojiCustomStorage(); return WebApp.connectHandlers.use('/emoji-custom/', async (req, res /* , next*/) => { const params = { emoji: decodeURIComponent(req.url.replace(/^\//, '').replace(/\?.*$/, '')) }; if (_.isEmpty(params.emoji)) { res.writeHead(403); res.write('Forbidden'); res.end(); return; } res.setHeader('Content-Disposition', 'inline'); const emoji = await EmojiCustom.findOneByName(params.emoji.split('.')[0], { projection: { _id: 1 } }); if (!emoji) { return writeSvgFallback(res, req); } const file = await RocketChatFileEmojiCustomInstance.getFileWithReadStream(encodeURIComponent(params.emoji)); if (!file) { // use code from username initials renderer until file upload is complete return writeSvgFallback(res, req); } const fileUploadDate = file.uploadDate != null ? file.uploadDate.toUTCString() : undefined; const reqModifiedHeader = req.headers['if-modified-since']; if (reqModifiedHeader != null && reqModifiedHeader === fileUploadDate) { res.setHeader('Last-Modified', reqModifiedHeader); res.writeHead(304); res.end(); return; } res.setHeader('Cache-Control', 'public, max-age=31536000'); res.setHeader('Last-Modified', fileUploadDate || new Date().toUTCString()); res.setHeader('Content-Length', file.length); if (/^svg$/i.test(params.emoji.split('.').pop())) { res.setHeader('Content-Type', 'image/svg+xml'); } else if (/^png$/i.test(params.emoji.split('.').pop())) { res.setHeader('Content-Type', 'image/png'); } else { res.setHeader('Content-Type', 'image/jpeg'); } file.readStream.pipe(res); }); }); settings.watchMultiple(['EmojiUpload_Storage_Type', 'EmojiUpload_FileSystemPath'], initializeEmojiCustomStorage);