diff --git a/src/Notifier.js b/src/Notifier.js index ca0a24d593..8c62a9e822 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -97,20 +97,24 @@ const Notifier = { }, getSoundForRoom: async function(roomId) { - // We do no caching here because the SDK caches the event content + // We do no caching here because the SDK caches setting // and the browser will cache the sound. let content = SettingsStore.getValue("notificationSound", roomId); if (!content) { - content = SettingsStore.getValue("notificationSound"); - if (!content) { - return null; - } + return null; } if (!content.url) { console.warn(`${roomId} has custom notification sound event, but no url key`); return null; } + + if (!content.url.startsWith("mxc://")) { + console.warn(`${roomId} has custom notification sound event, but url is not a mxc url`); + return null; + } + + // Ideally in here we could use MSC1310 to detect the type of file, and reject it. return { url: MatrixClientPeg.get().mxcUrlToHttp(content.url), @@ -123,7 +127,7 @@ const Notifier = { _playAudioNotification: async function(ev, room) { const sound = SettingsStore.isFeatureEnabled("feature_notification_sounds") ? await this.getSoundForRoom(room.roomId) : null; console.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`); - // XXX: How do we ensure this is a sound file and not going to be exploited? + try { const selector = document.querySelector(sound ? `audio[src='${sound.url}']` : "#messageAudio"); let audioElement = selector; diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.js b/src/components/views/settings/tabs/room/NotificationSettingsTab.js index 6199804cde..e7ed14b491 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.js +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.js @@ -20,12 +20,12 @@ import {_t} from "../../../../../languageHandler"; import MatrixClientPeg from "../../../../../MatrixClientPeg"; import AccessibleButton from "../../../elements/AccessibleButton"; import Notifier from "../../../../../Notifier"; -import SettingsStore from '../../../../../settings/SettingsStore'; +import SettingsStore from '../../../../../settings/SettingsStore'; +import { SettingLevel } from '../../../../../settings/SettingsStore'; export default class NotificationsSettingsTab extends React.Component { static propTypes = { roomId: PropTypes.string.isRequired, - closeSettingsFn: PropTypes.func.isRequired, }; constructor() { @@ -46,7 +46,7 @@ export default class NotificationsSettingsTab extends React.Component { }); } - _onSoundUploadChanged(e) { + async _onSoundUploadChanged(e) { if (!e.target.files || !e.target.files.length) { this.setState({ uploadedFile: null, @@ -58,11 +58,18 @@ export default class NotificationsSettingsTab extends React.Component { this.setState({ uploadedFile: file, }); + + try { + await this._saveSound(); + } catch (ex) { + console.error( + `Unable to save notification sound for ${this.props.roomId}`, + ex, + ); + } } - async _saveSound(e) { - e.stopPropagation(); - e.preventDefault(); + async _saveSound() { if (!this.state.uploadedFile) { return; } @@ -82,7 +89,8 @@ export default class NotificationsSettingsTab extends React.Component { await SettingsStore.setValue( "notificationSound", this.props.roomId, - "room-account", + SettingsStore. + SettingLevel.ROOM_ACCOUNT, { name: this.state.uploadedFile.name, type: type, @@ -101,7 +109,12 @@ export default class NotificationsSettingsTab extends React.Component { _clearSound(e) { e.stopPropagation(); e.preventDefault(); - SettingsStore.setValue("notificationSound", this.props.roomId, "room-account", null); + SettingsStore.setValue( + "notificationSound", + this.props.roomId, + SettingLevel.ROOM_ACCOUNT, + null, + ); this.setState({ currentSound: "default", @@ -119,11 +132,8 @@ export default class NotificationsSettingsTab extends React.Component {