diff --git a/ElectronClient/app/gui/EncryptionConfigScreen.jsx b/ElectronClient/app/gui/EncryptionConfigScreen.jsx index 117599a0a0..d922071748 100644 --- a/ElectronClient/app/gui/EncryptionConfigScreen.jsx +++ b/ElectronClient/app/gui/EncryptionConfigScreen.jsx @@ -101,9 +101,9 @@ class EncryptionConfigScreenComponent extends React.Component { let answer = null; if (isEnabled) { - answer = await dialogs.confirm(_('Disabling encryption means all your notes and attachments are going to be re-synchronised and sent unencrypted to the sync target. Do you wish to continue?')); + answer = await dialogs.confirm(_('Disabling encryption means *all* your notes and attachments are going to be re-synchronised and sent unencrypted to the sync target. Do you wish to continue?')); } else { - answer = await dialogs.prompt(_('Enabling encryption means all your notes and attachments are going to be re-synchronised and sent encrypted to the sync target. Do not lose the password as, for security purposes, this will be the only way to decrypt the data! To enable encryption, please enter your password below.'), '', '', { type: 'password' }); + answer = await dialogs.prompt(_('Enabling encryption means *all* your notes and attachments are going to be re-synchronised and sent encrypted to the sync target. Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.'), '', '', { type: 'password' }); } if (!answer) return; diff --git a/ReactNativeClient/lib/components/screen-header.js b/ReactNativeClient/lib/components/screen-header.js index 2a4e119b5c..cc653619cd 100644 --- a/ReactNativeClient/lib/components/screen-header.js +++ b/ReactNativeClient/lib/components/screen-header.js @@ -346,7 +346,7 @@ class ScreenHeaderComponent extends Component { menuOptionComponents.push( this.encryptionConfig_press()} key={'menuOption_encryptionConfig'} style={this.styles().contextMenuItem}> - {_('Encryption Configuration')} + {_('Encryption Config')} ); menuOptionComponents.push( diff --git a/ReactNativeClient/lib/components/screens/encryption-config.js b/ReactNativeClient/lib/components/screens/encryption-config.js index e947b1a418..94ccf9b721 100644 --- a/ReactNativeClient/lib/components/screens/encryption-config.js +++ b/ReactNativeClient/lib/components/screens/encryption-config.js @@ -1,5 +1,6 @@ const React = require('react'); const Component = React.Component; const { TextInput, TouchableOpacity, Linking, View, Switch, Slider, StyleSheet, Text, Button, ScrollView } = require('react-native'); +const EncryptionService = require('lib/services/EncryptionService'); const { connect } = require('react-redux'); const { ScreenHeader } = require('lib/components/screen-header.js'); const { _ } = require('lib/locale.js'); @@ -9,6 +10,8 @@ const { themeStyle } = require('lib/components/global-style.js'); const { time } = require('lib/time-utils.js'); const Setting = require('lib/models/Setting.js'); const shared = require('lib/components/shared/encryption-config-shared.js'); +const { dialogs } = require('lib/dialogs.js'); +const DialogBox = require('react-native-dialogbox').default; class EncryptionConfigScreenComponent extends BaseScreenComponent { @@ -19,6 +22,11 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { constructor() { super(); + this.state = { + passwordPromptShow: false, + passwordPromptAnswer: '', + }; + shared.constructor(this); this.styles_ = {}; @@ -67,6 +75,8 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { fontSize: theme.fontSize, paddingTop: 5, paddingBottom: 5, + marginTop: theme.marginTop, + marginBottom: 5, }, normalText: { flex: 1, @@ -103,7 +113,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { {_('Created: %s', time.formatMsToLocal(mk.created_time))} {_('Password:')} - onPasswordChange(text)} style={{flex:1, marginRight: 10}}> + onPasswordChange(text)} style={{flex:1, marginRight: 10}}> {passwordOk} @@ -111,6 +121,36 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { ); } + passwordPromptComponent() { + const theme = themeStyle(this.props.theme); + + const onEnableClick = async () => { + try { + const password = this.state.passwordPromptAnswer; + if (!password) throw new Error(_('Password cannot be empty')); + await EncryptionService.instance().generateMasterKeyAndEnableEncryption(password); + this.setState({ passwordPromptShow: false }); + } catch (error) { + await dialogs.error(this, error.message); + } + } + + return ( + + {_('Enabling encryption means *all* your notes and attachments are going to be re-synchronised and sent encrypted to the sync target. Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.')} + { this.setState({ passwordPromptAnswer: text }) }}> + + + + + + + + + + ); + } + render() { const masterKeys = this.state.masterKeys; const decryptedItemsInfo = this.props.encryptionEnabled ? {shared.decryptedStatText(this)} : null; @@ -121,15 +161,40 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { mkComps.push(this.renderMasterKey(i+1, mk)); } + const onToggleButtonClick = async () => { + if (this.props.encryptionEnabled) { + const ok = await dialogs.confirm(this, _('Disabling encryption means *all* your notes and attachments are going to be re-synchronised and sent unencrypted to the sync target. Do you wish to continue?')); + if (!ok) return; + + try { + await EncryptionService.instance().disableEncryption(); + } catch (error) { + await dialogs.error(this, error.message); + } + } else { + this.setState({ + passwordPromptShow: true, + passwordPromptAnswer: '', + }); + return; + } + }; + + const passwordPromptComp = this.state.passwordPromptShow ? this.passwordPromptComponent() : null; + const toggleButton = !this.state.passwordPromptShow ? : null; + return ( - + {_('Status')} {_('Encryption is: %s', this.props.encryptionEnabled ? _('Enabled') : _('Disabled'))} {decryptedItemsInfo} + {toggleButton} + {passwordPromptComp} {mkComps} + { this.dialogbox = dialogbox }}/> ); } diff --git a/ReactNativeClient/lib/components/shared/encryption-config-shared.js b/ReactNativeClient/lib/components/shared/encryption-config-shared.js index 3f97877985..ad57ddc6a1 100644 --- a/ReactNativeClient/lib/components/shared/encryption-config-shared.js +++ b/ReactNativeClient/lib/components/shared/encryption-config-shared.js @@ -58,7 +58,6 @@ shared.checkPasswords = async function(comp) { const ok = password ? await EncryptionService.instance().checkMasterKeyPassword(mk, password) : false; passwordChecks[mk.id] = ok; } - console.info(passwordChecks); comp.setState({ passwordChecks: passwordChecks }); } diff --git a/ReactNativeClient/lib/synchronizer.js b/ReactNativeClient/lib/synchronizer.js index b87b8d463e..26493ef620 100644 --- a/ReactNativeClient/lib/synchronizer.js +++ b/ReactNativeClient/lib/synchronizer.js @@ -214,7 +214,9 @@ class Synchronizer { let ItemClass = BaseItem.itemClass(local); let path = BaseItem.systemPath(local); - // Safety check to avoid infinite loops: + // Safety check to avoid infinite loops. + // In fact this error is possible if the item is marked for sync (via sync_time or force_sync) while synchronisation is in + // progress. In that case exit anyway to be sure we aren't in a loop and the item will be re-synced next time. if (donePaths.indexOf(path) > 0) throw new Error(sprintf('Processing a path that has already been done: %s. sync_time was not updated?', path)); let remote = await this.api().stat(path); diff --git a/ReactNativeClient/root.js b/ReactNativeClient/root.js index 4645690c5d..d95da92185 100644 --- a/ReactNativeClient/root.js +++ b/ReactNativeClient/root.js @@ -281,8 +281,12 @@ async function initialize(dispatch) { const mainLogger = new Logger(); mainLogger.addTarget('database', { database: logDatabase, source: 'm' }); - if (Setting.value('env') == 'dev') mainLogger.addTarget('console'); - mainLogger.setLevel(Logger.LEVEL_DEBUG); + mainLogger.setLevel(Logger.LEVEL_INFO); + + if (Setting.value('env') == 'dev') { + mainLogger.addTarget('console'); + mainLogger.setLevel(Logger.LEVEL_DEBUG); + } reg.setLogger(mainLogger); reg.setShowErrorMessageBoxHandler((message) => { alert(message) }); @@ -345,7 +349,7 @@ async function initialize(dispatch) { await Setting.load(); if (Setting.value('firstStart')) { - const locale = NativeModules.I18nManager.localeIdentifier + let locale = NativeModules.I18nManager.localeIdentifier if (!locale) locale = defaultLocale(); Setting.setValue('locale', closestSupportedLocale(locale)); if (Setting.value('env') === 'dev') Setting.setValue('sync.target', SyncTargetRegistry.nameToId('onedrive_dev'));