Skip to content
Snippets Groups Projects
avatar.js 7.41 KiB
Newer Older
import getFileType from 'file-type';

Meteor.startup(function() {
	let storeType = 'GridFS';

	if (RocketChat.settings.get('Accounts_AvatarStoreType')) {
		storeType = RocketChat.settings.get('Accounts_AvatarStoreType');
	}

	const RocketChatStore = RocketChatFile[storeType];

	if (!RocketChatStore) {
		throw new Error(`Invalid RocketChatStore type [${ storeType }]`);
	console.log((`Using ${ storeType } for Avatar storage`).green);

	function transformWrite(file, readStream, writeStream) {
		if (RocketChatFile.enabled === false || RocketChat.settings.get('Accounts_AvatarResize') !== true) {
			return readStream.pipe(writeStream);
		}
		const height = RocketChat.settings.get('Accounts_AvatarSize');
		const width = height;
		return RocketChatFile.gm(readStream, file.fileName).background('#ffffff').resize(width, `${ height }^`).gravity('Center').crop(width, height).extent(width, height).stream('jpeg').pipe(writeStream);
	}

	let path = '~/uploads';
	if (RocketChat.settings.get('Accounts_AvatarStorePath') && RocketChat.settings.get('Accounts_AvatarStorePath').trim() !== '') {
		path = RocketChat.settings.get('Accounts_AvatarStorePath');
	}

	this.RocketChatFileAvatarInstance = new RocketChatStore({
		name: 'avatars',
		absolutePath: path,
	WebApp.connectHandlers.use('/avatar/', Meteor.bindEnvironment(function(req, res/*, next*/) {
		const params = {
			username: decodeURIComponent(req.url.replace(/^\//, '').replace(/\?.*$/, ''))
		};

		if (_.isEmpty(params.username)) {
			res.writeHead(403);
			res.write('Forbidden');
			res.end();
			return;
		}

		return getNewAvatar(params, req, res);
	}));
});
function getNewAvatar(params, req, res) {
	const match = /^\/([^?]*)/.exec(req.url);

	if (match[1]) {
		let username = decodeURIComponent(match[1]);
		const file = RocketChat.models.Uploads.findOneByUsername(username);

		if (file) {
			res.setHeader('Content-Security-Policy', 'default-src \'none\'');
			return FileUpload.get(file, req, res);
		} else {
			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');

Kiran Rao's avatar
Kiran Rao committed
			const reqModifiedHeader = req.headers['if-modified-since'];

			if (reqModifiedHeader) {
				if (reqModifiedHeader === 'Thu, 01 Jan 2015 00:00:00 GMT') {
					res.writeHead(304);
					res.end();
					return;
				}
			}

			const colors = ['#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFC107', '#FF9800', '#FF5722', '#795548', '#9E9E9E', '#607D8B'];

			if (RocketChat.settings.get('UI_Use_Name_Avatar')) {
				const user = RocketChat.models.Users.findOneByUsername(username, {
					fields: {
						name: 1
					}
				});

				if (user && user.name) {
					username = user.name;
				}
			}

			let color = '';
			let initials = '';

			if (username === '?') {
				color = '#000';
				initials = username;
			} else {
				const position = username.length % colors.length;

				color = colors[position];
				username = username.replace(/[^A-Za-z0-9]/g, '.').replace(/\.+/g, '.').replace(/(^\.)|(\.$)/g, '');

				const usernameParts = username.split('.');

				initials = usernameParts.length > 1 ? _.first(usernameParts)[0] + _.last(usernameParts)[0] : username.replace(/[^A-Za-z0-9]/g, '').substr(0, 2);
				initials = initials.toUpperCase();
			}

			const svg = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" pointer-events=\"none\" width=\"50\" height=\"50\" style=\"width: 50px; height: 50px; background-color: ${ color };\">\n	<text text-anchor=\"middle\" y=\"50%\" x=\"50%\" dy=\"0.36em\" pointer-events=\"auto\" fill=\"#ffffff\" font-family=\"Helvetica, Arial, Lucida Grande, sans-serif\" style=\"font-weight: 400; font-size: 28px;\">\n		${ initials }\n	</text>\n</svg>`;
			res.write(svg);
			res.end();

			return;
		}
	}

	res.writeHead(404);
	res.end();
	return;
}

function getOldAvatar(params, req, res) {
	let username = params.username.replace(/\.jpg$/, '').replace(/^@/, '');
	let file;
	if (params.username[0] !== '@') {
		if (Meteor.settings && Meteor.settings.public && Meteor.settings.public.sandstorm) {
			const user = RocketChat.models.Users.findOneByUsername(username);
			if (user && user.services && user.services.sandstorm && user.services.sandstorm.picture) {
				res.setHeader('Location', user.services.sandstorm.picture);
				res.writeHead(302);
				res.end();
				return;
			}
		}

		file = RocketChatFileAvatarInstance.getFileWithReadStream(encodeURIComponent(`${ username }.jpg`));
	}

	res.setHeader('Content-Disposition', 'inline');
	if (!file) {
		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');
Kiran Rao's avatar
Kiran Rao committed
		const reqModifiedHeader = req.headers['if-modified-since'];
		if (reqModifiedHeader) {
			if (reqModifiedHeader === 'Thu, 01 Jan 2015 00:00:00 GMT') {
				res.writeHead(304);
				res.end();
				return;
			}
		}

		const colors = ['#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFC107', '#FF9800', '#FF5722', '#795548', '#9E9E9E', '#607D8B'];
		if (RocketChat.settings.get('UI_Use_Name_Avatar')) {
			const user = RocketChat.models.Users.findOneByUsername(username, {
				fields: {
					name: 1

			if (user && user.name) {
				username = user.name;
			}
		let color = '';
		let initials = '';

		if (username === '?') {
			color = '#000';
			initials = username;
		} else {
			const position = username.length % colors.length;

			color = colors[position];
			username = username.replace(/[^A-Za-z0-9]/g, '.').replace(/\.+/g, '.').replace(/(^\.)|(\.$)/g, '');

			const usernameParts = username.split('.');

			initials = usernameParts.length > 1 ? _.first(usernameParts)[0] + _.last(usernameParts)[0] : username.replace(/[^A-Za-z0-9]/g, '').substr(0, 2);
			initials = initials.toUpperCase();
		}

		const svg = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" pointer-events=\"none\" width=\"50\" height=\"50\" style=\"width: 50px; height: 50px; background-color: ${ color };\">\n	<text text-anchor=\"middle\" y=\"50%\" x=\"50%\" dy=\"0.36em\" pointer-events=\"auto\" fill=\"#ffffff\" font-family=\"Helvetica, Arial, Lucida Grande, sans-serif\" style=\"font-weight: 400; font-size: 28px;\">\n		${ initials }\n	</text>\n</svg>`;
		res.write(svg);
		res.end();

		return;
	}

	const reqModifiedHeader = req.headers['if-modified-since'];
	if (reqModifiedHeader) {
		if (reqModifiedHeader === (file.uploadDate && file.uploadDate.toUTCString())) {
			res.setHeader('Last-Modified', reqModifiedHeader);
			res.writeHead(304);
			res.end();
			return;
		}
	}

	res.setHeader('Cache-Control', 'public, max-age=0');
	res.setHeader('Expires', '-1');
	res.setHeader('Last-Modified', (file.uploadDate && file.uploadDate.toUTCString()) || new Date().toUTCString());
	res.setHeader('Content-Length', file.length);

	if (file.contentType) {
		res.setHeader('Content-Type', file.contentType);
	} else {
		file.readStream.once('data', function(chunk) {
			const fileType = getFileType(chunk);
			if (fileType) {
				return res.setHeader('Content-Type', fileType.mime);
			} else {
				return res.setHeader('Content-Type', 'image/jpeg');
			}
		});
	}

	file.readStream.pipe(res);
}