2-Step Verification (Google Authenticator)

Small fixes
This commit is contained in:
RainLoop Team 2014-04-05 23:48:22 +04:00
parent cae0cc2f77
commit 419b47a81e
51 changed files with 1747 additions and 419 deletions

View file

@ -285,6 +285,7 @@ module.exports = function (grunt) {
"dev/ViewModels/PopupsComposeOpenPgpViewModel.js",
"dev/ViewModels/PopupsIdentityViewModel.js",
"dev/ViewModels/PopupsLanguagesViewModel.js",
"dev/ViewModels/PopupsTwoFactorTestViewModel.js",
"dev/ViewModels/PopupsAskViewModel.js",
"dev/ViewModels/LoginViewModel.js",
@ -305,6 +306,7 @@ module.exports = function (grunt) {
"dev/Settings/Accounts.js",
"dev/Settings/Identity.js",
"dev/Settings/Identities.js",
"dev/Settings/Security.js",
"dev/Settings/Social.js",
"dev/Settings/ChangePassword.js",
"dev/Settings/Folders.js",

View file

@ -7,6 +7,7 @@ function AdminSecurity()
{
this.csrfProtection = ko.observable(!!RL.settingsGet('UseTokenProtection'));
this.openPGP = ko.observable(!!RL.settingsGet('OpenPGP'));
this.allowTwoFactorAuth = ko.observable(!!RL.settingsGet('AllowTwoFactorAuth'));
this.adminLogin = ko.observable(RL.settingsGet('AdminLogin'));
this.adminPassword = ko.observable('');
@ -72,6 +73,12 @@ AdminSecurity.prototype.onBuild = function ()
'OpenPGP': bValue ? '1' : '0'
});
});
this.allowTwoFactorAuth.subscribe(function (bValue) {
RL.remote().saveAdminConfig(Utils.emptyFunction, {
'AllowTwoFactorAuth': bValue ? '1' : '0'
});
});
};
AdminSecurity.prototype.onHide = function ()

View file

@ -937,6 +937,11 @@ RainLoopApp.prototype.bootstart = function ()
Utils.removeSettingsViewModel(SettingsOpenPGP);
}
if (!RL.settingsGet('AllowTwoFactorAuth'))
{
Utils.removeSettingsViewModel(SettingsSecurity);
}
if (!bGoogle && !bFacebook && !bTwitter)
{
Utils.removeSettingsViewModel(SettingsSocialScreen);

View file

@ -317,6 +317,9 @@ Enums.Notification = {
'DomainNotAllowed': 109,
'AccountNotAllowed': 110,
'AccountTwoFactorAuthRequired': 120,
'AccountTwoFactorAuthError': 121,
'CantGetMessageList': 201,
'CantGetMessage': 202,
'CantDeleteMessage': 203,

View file

@ -179,7 +179,7 @@ NewHtmlEditorWrapper.prototype.init = function ()
{
self.editor.on('instanceReady', function () {
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65/* A */, 'selectAll');
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65 /* A */, 'selectAll');
self.fOnReady();
self.__resizable = true;

View file

@ -568,6 +568,9 @@ Utils.initNotificationLanguage = function ()
NotificationI18N[Enums.Notification.DomainNotAllowed] = Utils.i18n('NOTIFICATIONS/DOMAIN_NOT_ALLOWED');
NotificationI18N[Enums.Notification.AccountNotAllowed] = Utils.i18n('NOTIFICATIONS/ACCOUNT_NOT_ALLOWED');
NotificationI18N[Enums.Notification.AccountTwoFactorAuthRequired] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_REQUIRED');
NotificationI18N[Enums.Notification.AccountTwoFactorAuthError] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_ERROR');
NotificationI18N[Enums.Notification.CantGetMessageList] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST');
NotificationI18N[Enums.Notification.CantGetMessage] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE');
NotificationI18N[Enums.Notification.CantDeleteMessage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_MESSAGE');
@ -672,14 +675,14 @@ Utils.delegateRun = function (oObject, sMethodName, aParameters, nDelay)
Utils.killCtrlAandS = function (oEvent)
{
oEvent = oEvent || window.event;
if (oEvent)
if (oEvent && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey)
{
var
oSender = oEvent.target || oEvent.srcElement,
iKey = oEvent.keyCode || oEvent.which
;
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.S)
if (iKey === Enums.EventKeyCode.S)
{
oEvent.preventDefault();
return;
@ -690,7 +693,7 @@ Utils.killCtrlAandS = function (oEvent)
return;
}
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.A)
if (iKey === Enums.EventKeyCode.A)
{
if (window.getSelection)
{
@ -1716,48 +1719,3 @@ Utils.selectElement = function (element)
}
/* jshint onevar: true */
};
Utils.openPgpImportPublicKeys = function (sPublicKeysArmored)
{
if (window.openpgp && RL)
{
var
oOpenpgpKeyring = RL.data().openpgpKeyring,
oImported = window.openpgp.key.readArmored(sPublicKeysArmored)
;
if (oOpenpgpKeyring && oImported && !oImported.err && Utils.isArray(oImported.keys) &&
0 < oImported.keys.length)
{
_.each(oImported.keys, function (oPrivKey) {
if (oPrivKey)
{
window.console.log(oPrivKey);
// var oKey = oOpenpgpKeyring.getKeysForKeyId(oPrivKey.primaryKey.getFingerprint());
// if (oKey && oKey[0])
// {
// if (oKey[0].isPublic())
// {
// oPrivKey.update(oKey[0]);
// oOpenpgpKeyring.publicKeys.removeForId(oPrivKey.primaryKey.getFingerprint());
// oOpenpgpKeyring.privateKeys.push(oPrivKey);
// }
// else
// {
// oKey[0].update(oPrivKey);
// }
// }
// else
// {
// oOpenpgpKeyring.importKey(oPrivKey.armored);
// }
}
});
oOpenpgpKeyring.store();
return true;
}
}
return false;
};

View file

@ -151,15 +151,14 @@ FolderModel.prototype.initComputed = function ()
{
return '' + iCount;
}
else if (0 < iUnread && Enums.FolderType.Trash !== iType && Enums.FolderType.SentItems !== iType)
else if (0 < iUnread && Enums.FolderType.Trash !== iType && Enums.FolderType.Archive !== iType && Enums.FolderType.SentItems !== iType)
{
return '' + iUnread;
}
}
return '';
// return 0 < iUnread && (Enums.FolderType.Inbox === iType || Enums.FolderType.Spam === iType) ? '' + iUnread :
// (0 < iCount && Enums.FolderType.Draft === iType ? '' + iCount : '');
}, this);
this.canBeDeleted = ko.computed(function () {

120
dev/Settings/Security.js Normal file
View file

@ -0,0 +1,120 @@
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
/**
* @constructor
*/
function SettingsSecurity()
{
this.processing = ko.observable(false);
this.clearing = ko.observable(false);
this.viewUser = ko.observable('');
this.viewEnable = ko.observable(false);
this.viewEnable.subs = true;
this.twoFactorStatus = ko.observable(false);
this.viewSecret = ko.observable('');
this.viewBackupCodes = ko.observable('');
this.viewUrl = ko.observable('');
this.bFirst = true;
this.viewTwoFactorStatus = ko.computed(function () {
Globals.langChangeTrigger();
return Utils.i18n(
this.twoFactorStatus() ?
'SETTINGS_SECURITY/TWO_FACTOR_SECRET_CONFIGURED_DESC' :
'SETTINGS_SECURITY/TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC'
);
}, this);
this.onResult = _.bind(this.onResult, this);
}
Utils.addSettingsViewModel(SettingsSecurity, 'SettingsSecurity', 'SETTINGS_LABELS/LABEL_SECURITY_NAME', 'security');
SettingsSecurity.prototype.createTwoFactor = function ()
{
this.processing(true);
RL.remote().createTwoFactor(this.onResult);
};
SettingsSecurity.prototype.enableTwoFactor = function ()
{
this.processing(true);
RL.remote().enableTwoFactor(this.onResult, this.viewEnable());
};
SettingsSecurity.prototype.testTwoFactor = function ()
{
kn.showScreenPopup(PopupsTwoFactorTestViewModel);
};
SettingsSecurity.prototype.clearTwoFactor = function ()
{
this.viewSecret('');
this.viewBackupCodes('');
this.viewUrl('');
this.clearing(true);
RL.remote().clearTwoFactor(this.onResult);
};
SettingsSecurity.prototype.onShow = function ()
{
this.viewSecret('');
this.viewBackupCodes('');
this.viewUrl('');
};
SettingsSecurity.prototype.onResult = function (sResult, oData)
{
this.processing(false);
this.clearing(false);
if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
{
this.viewUser(Utils.pString(oData.Result.User));
this.viewEnable(!!oData.Result.Enable);
this.twoFactorStatus(!!oData.Result.IsSet);
this.viewSecret(Utils.pString(oData.Result.Secret));
this.viewBackupCodes(Utils.pString(oData.Result.BackupCodes).replace(/[\s]+/g, ' '));
this.viewUrl(Utils.pString(oData.Result.Url));
}
else
{
this.viewUser('');
this.viewEnable(false);
this.twoFactorStatus(false);
this.viewSecret('');
this.viewBackupCodes('');
this.viewUrl('');
}
if (this.bFirst)
{
this.bFirst = false;
var self = this;
this.viewEnable.subscribe(function (bValue) {
if (this.viewEnable.subs)
{
RL.remote().enableTwoFactor(function (sResult, oData) {
if (Enums.StorageResultType.Success !== sResult || !oData || !oData.Result)
{
self.viewEnable.subs = false;
self.viewEnable(false);
self.viewEnable.subs = true;
}
}, bValue);
}
}, this);
}
};
SettingsSecurity.prototype.onBuild = function ()
{
this.processing(true);
RL.remote().getTwoFactor(this.onResult);
};

View file

@ -34,18 +34,74 @@ WebMailAjaxRemoteStorage.prototype.folders = function (fCallback)
* @param {string} sPassword
* @param {boolean} bSignMe
* @param {string=} sLanguage
* @param {string=} sAdditionalCode
*/
WebMailAjaxRemoteStorage.prototype.login = function (fCallback, sEmail, sLogin, sPassword, bSignMe, sLanguage)
WebMailAjaxRemoteStorage.prototype.login = function (fCallback, sEmail, sLogin, sPassword, bSignMe, sLanguage, sAdditionalCode)
{
this.defaultRequest(fCallback, 'Login', {
'Email': sEmail,
'Login': sLogin,
'Password': sPassword,
'Language': sLanguage || '',
'AdditionalCode': sAdditionalCode || '',
'SignMe': bSignMe ? '1' : '0'
});
};
/**
* @param {?Function} fCallback
*/
WebMailAjaxRemoteStorage.prototype.getTwoFactor = function (fCallback)
{
this.defaultRequest(fCallback, 'GetTwoFactorInfo');
};
/**
* @param {?Function} fCallback
*/
WebMailAjaxRemoteStorage.prototype.createTwoFactor = function (fCallback)
{
this.defaultRequest(fCallback, 'CreateTwoFactorSecret');
};
/**
* @param {?Function} fCallback
*/
WebMailAjaxRemoteStorage.prototype.clearTwoFactor = function (fCallback)
{
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
};
/**
* @param {?Function} fCallback
* @param {string} sCode
*/
WebMailAjaxRemoteStorage.prototype.testTwoFactor = function (fCallback, sCode)
{
this.defaultRequest(fCallback, 'TestTwoFactorInfo', {
'Code': sCode
});
};
/**
* @param {?Function} fCallback
* @param {boolean} bEnable
*/
WebMailAjaxRemoteStorage.prototype.enableTwoFactor = function (fCallback, bEnable)
{
this.defaultRequest(fCallback, 'EnableTwoFactor', {
'Enable': bEnable ? '1' : '0'
});
};
/**
* @param {?Function} fCallback
*/
WebMailAjaxRemoteStorage.prototype.clearTwoFactorInfo = function (fCallback)
{
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
};
/**
* @param {?Function} fCallback
* @param {string} sEmail

View file

@ -72,6 +72,9 @@ function WebMailDataStorage()
this.signatureToAll = ko.observable(false);
this.replyTo = ko.observable('');
// security
this.enableTwoFactor = ko.observable(false);
// accounts
this.accounts = ko.observableArray([]);
this.accountsLoading = ko.observable(false).extend({'throttle': 100});
@ -434,6 +437,7 @@ WebMailDataStorage.prototype.populateDataOnStart = function()
this.replyTo(RL.settingsGet('ReplyTo'));
this.signature(RL.settingsGet('Signature'));
this.signatureToAll(!!RL.settingsGet('SignatureToAll'));
this.enableTwoFactor(!!RL.settingsGet('EnableTwoFactor'));
this.lastFoldersHash = RL.local().get(Enums.ClientSideKeyName.FoldersLashHash) || '';

View file

@ -49,6 +49,7 @@
@import "Languages.less";
@import "AddAccount.less";
@import "OpenPgpKey.less";
@import "TwoFactor.less";
@import "Identity.less";
@import "AdvancedSearch.less";
@import "MessageList.less";

View file

@ -0,0 +1,7 @@
.popups {
.b-two-factor-test-content {
.modal-header {
background-color: #fff;
}
}
}

View file

@ -15,6 +15,11 @@ function LoginViewModel()
this.password = ko.observable('');
this.signMe = ko.observable(false);
this.additionalCode = ko.observable('');
this.additionalCode.error = ko.observable(false);
this.additionalCode.focused = ko.observable(false);
this.additionalCode.visibility = ko.observable(false);
this.logoImg = Utils.trim(RL.settingsGet('LoginLogo'));
this.loginDescription = Utils.trim(RL.settingsGet('LoginDescription'));
this.logoCss = Utils.trim(RL.settingsGet('LoginCss'));
@ -29,6 +34,8 @@ function LoginViewModel()
this.email.subscribe(function () {
this.emailError(false);
this.additionalCode('');
this.additionalCode.visibility(false);
}, this);
this.login.subscribe(function () {
@ -39,6 +46,14 @@ function LoginViewModel()
this.passwordError(false);
}, this);
this.additionalCode.subscribe(function () {
this.additionalCode.error(false);
}, this);
this.additionalCode.visibility.subscribe(function () {
this.additionalCode.error(false);
}, this);
this.submitRequest = ko.observable(false);
this.submitError = ko.observable('');
@ -68,7 +83,12 @@ function LoginViewModel()
this.emailError('' === Utils.trim(this.email()));
this.passwordError('' === Utils.trim(this.password()));
if (this.emailError() || this.passwordError())
if (this.additionalCode.visibility())
{
this.additionalCode.error('' === Utils.trim(this.additionalCode()));
}
if (this.emailError() || this.passwordError() || this.additionalCode.error())
{
return false;
}
@ -81,12 +101,28 @@ function LoginViewModel()
{
if (oData.Result)
{
RL.loginAndLogoutReload();
if (oData.TwoFactorAuth)
{
this.additionalCode('');
this.additionalCode.visibility(true);
this.additionalCode.focused(true);
this.submitRequest(false);
}
else
{
RL.loginAndLogoutReload();
}
}
else if (oData.ErrorCode)
{
this.submitRequest(false);
this.submitError(Utils.getNotification(oData.ErrorCode));
if ('' === this.submitError())
{
this.submitError(Utils.getNotification(Enums.Notification.UnknownError));
}
}
else
{
@ -100,7 +136,8 @@ function LoginViewModel()
}
}, this), this.email(), this.login(), this.password(), !!this.signMe(),
this.bSendLanguage ? this.mainLanguage() : '');
this.bSendLanguage ? this.mainLanguage() : '',
this.additionalCode.visibility() ? this.additionalCode() : '');
return true;

View file

@ -929,12 +929,12 @@ PopupsComposeViewModel.prototype.onBuild = function ()
if (oEvent && self.modalVisibility() && RL.data().useKeyboardShortcuts())
{
if (self.bAllowCtrlS && oEvent.ctrlKey && Enums.EventKeyCode.S === oEvent.keyCode)
if (self.bAllowCtrlS && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.S === oEvent.keyCode)
{
self.saveCommand();
bResult = false;
}
else if (oEvent.ctrlKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
else if (oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
{
self.sendCommand();
bResult = false;

View file

@ -0,0 +1,55 @@
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
/**
* @constructor
* @extends KnoinAbstractViewModel
*/
function PopupsTwoFactorTestViewModel()
{
KnoinAbstractViewModel.call(this, 'Popups', 'PopupsTwoFactorTest');
var self = this;
this.code = ko.observable('');
this.code.focused = ko.observable(false);
this.code.status = ko.observable(null);
this.testing = ko.observable(false);
// commands
this.testCode = Utils.createCommand(this, function () {
this.testing(true);
RL.remote().testTwoFactor(function (sResult, oData) {
self.testing(false);
self.code.status(Enums.StorageResultType.Success === sResult && oData && oData.Result ? true : false);
}, this.code());
}, function () {
return '' !== this.code() && !this.testing();
});
Knoin.constructorEnd(this);
}
Utils.extendAsViewModel('PopupsTwoFactorTestViewModel', PopupsTwoFactorTestViewModel);
PopupsTwoFactorTestViewModel.prototype.clearPopup = function ()
{
this.code('');
this.code.focused(false);
this.code.status(null);
this.testing(false);
};
PopupsTwoFactorTestViewModel.prototype.onShow = function ()
{
this.clearPopup();
};
PopupsTwoFactorTestViewModel.prototype.onFocus = function ()
{
this.code.focused(true);
};

View file

@ -1,8 +1,8 @@
{
"name": "RainLoop",
"title": "RainLoop Webmail",
"version": "1.6.3",
"release": "804",
"version": "1.6.4",
"release": "890",
"description": "Simple, modern & fast web-based email client",
"homepage": "http://rainloop.net",
"main": "Gruntfile.js",

View file

@ -0,0 +1,201 @@
<?php
/**
* PHP Class for handling Google Authenticator 2-factor authentication
*
* @author Michael Kliewe
* @copyright 2012 Michael Kliewe
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://www.phpgangsta.de/
*/
class PHPGangsta_GoogleAuthenticator
{
protected $_codeLength = 6;
/**
* Create new secret.
* 16 characters, randomly chosen from the allowed base32 characters.
*
* @param int $secretLength
* @return string
*/
public function createSecret($secretLength = 16)
{
$validChars = $this->_getBase32LookupTable();
unset($validChars[32]);
$secret = '';
for ($i = 0; $i < $secretLength; $i++) {
$secret .= $validChars[array_rand($validChars)];
}
return $secret;
}
/**
* Calculate the code, with given secret and point in time
*
* @param string $secret
* @param int|null $timeSlice
* @return string
*/
public function getCode($secret, $timeSlice = null)
{
if ($timeSlice === null) {
$timeSlice = floor(time() / 30);
}
$secretkey = $this->_base32Decode($secret);
// Pack time into binary string
$time = chr(0).chr(0).chr(0).chr(0).pack('N*', $timeSlice);
// Hash it with users secret key
$hm = hash_hmac('SHA1', $time, $secretkey, true);
// Use last nipple of result as index/offset
$offset = ord(substr($hm, -1)) & 0x0F;
// grab 4 bytes of the result
$hashpart = substr($hm, $offset, 4);
// Unpak binary value
$value = unpack('N', $hashpart);
$value = $value[1];
// Only 32 bits
$value = $value & 0x7FFFFFFF;
$modulo = pow(10, $this->_codeLength);
return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT);
}
/**
* Get QR-Code URL for image, from google charts
*
* @param string $name
* @param string $secret
* @return string
*/
public function getQRCodeGoogleUrl($name, $secret) {
$urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.$urlencoded.'';
}
/**
* Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now
*
* @param string $secret
* @param string $code
* @param int $discrepancy This is the allowed time drift in 30 second units (8 means 4 minutes before or after)
* @return bool
*/
public function verifyCode($secret, $code, $discrepancy = 1)
{
$currentTimeSlice = floor(time() / 30);
for ($i = -$discrepancy; $i <= $discrepancy; $i++) {
$calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
if ($calculatedCode == $code ) {
return true;
}
}
return false;
}
/**
* Set the code length, should be >=6
*
* @param int $length
* @return PHPGangsta_GoogleAuthenticator
*/
public function setCodeLength($length)
{
$this->_codeLength = $length;
return $this;
}
/**
* Helper class to decode base32
*
* @param $secret
* @return bool|string
*/
protected function _base32Decode($secret)
{
if (empty($secret)) return '';
$base32chars = $this->_getBase32LookupTable();
$base32charsFlipped = array_flip($base32chars);
$paddingCharCount = substr_count($secret, $base32chars[32]);
$allowedValues = array(6, 4, 3, 1, 0);
if (!in_array($paddingCharCount, $allowedValues)) return false;
for ($i = 0; $i < 4; $i++){
if ($paddingCharCount == $allowedValues[$i] &&
substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i])) return false;
}
$secret = str_replace('=','', $secret);
$secret = str_split($secret);
$binaryString = "";
for ($i = 0; $i < count($secret); $i = $i+8) {
$x = "";
if (!in_array($secret[$i], $base32chars)) return false;
for ($j = 0; $j < 8; $j++) {
$x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
}
$eightBits = str_split($x, 8);
for ($z = 0; $z < count($eightBits); $z++) {
$binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:"";
}
}
return $binaryString;
}
/**
* Helper class to encode base32
*
* @param string $secret
* @param bool $padding
* @return string
*/
protected function _base32Encode($secret, $padding = true)
{
if (empty($secret)) return '';
$base32chars = $this->_getBase32LookupTable();
$secret = str_split($secret);
$binaryString = "";
for ($i = 0; $i < count($secret); $i++) {
$binaryString .= str_pad(base_convert(ord($secret[$i]), 10, 2), 8, '0', STR_PAD_LEFT);
}
$fiveBitBinaryArray = str_split($binaryString, 5);
$base32 = "";
$i = 0;
while ($i < count($fiveBitBinaryArray)) {
$base32 .= $base32chars[base_convert(str_pad($fiveBitBinaryArray[$i], 5, '0'), 2, 10)];
$i++;
}
if ($padding && ($x = strlen($binaryString) % 40) != 0) {
if ($x == 8) $base32 .= str_repeat($base32chars[32], 6);
elseif ($x == 16) $base32 .= str_repeat($base32chars[32], 4);
elseif ($x == 24) $base32 .= str_repeat($base32chars[32], 3);
elseif ($x == 32) $base32 .= $base32chars[32];
}
return $base32;
}
/**
* Get array with all 32 characters for decoding from/encoding to base32
*
* @return array
*/
protected function _getBase32LookupTable()
{
return array(
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
'=' // padding char
);
}
}

View file

@ -509,7 +509,7 @@ class Actions
if (null === $this->oTwoFactorAuthProvider)
{
$this->oTwoFactorAuthProvider = new \RainLoop\Providers\TwoFactorAuth(
$this->fabrica('two-factor-auth')
$this->Config()->Get('security', 'allow_two_factor_auth', false) ? $this->fabrica('two-factor-auth') : null
);
}
@ -932,6 +932,7 @@ class Actions
'LoginCss' => $oConfig->Get('branding', 'login_css', ''),
'Token' => $oConfig->Get('security', 'csrf_protection', false) ? \RainLoop\Utils::GetCsrfToken() : '',
'OpenPGP' => $oConfig->Get('security', 'openpgp', false),
'AllowTwoFactorAuth' => (bool) $oConfig->Get('security', 'allow_two_factor_auth', false),
'InIframe' => (bool) $oConfig->Get('labs', 'in_iframe', false),
'AllowAdminPanel' => (bool) $oConfig->Get('security', 'allow_admin_panel', true),
'AllowHtmlEditorSourceButton' => (bool) $oConfig->Get('labs', 'allow_html_editor_source_button', false),
@ -1141,6 +1142,7 @@ class Actions
$aResult['ReplyTo'] = '';
$aResult['Signature'] = '';
$aResult['SignatureToAll'] = false;
$aResult['EnableTwoFactor'] = false;
$aResult['ParentEmail'] = '';
$aResult['InterfaceAnimation'] = \RainLoop\Enumerations\InterfaceAnimation::NORMAL;
$aResult['CustomThemeType'] = \RainLoop\Enumerations\CustomThemeType::LIGHT;
@ -1181,6 +1183,7 @@ class Actions
$aResult['ReplyTo'] = $oSettings->GetConf('ReplyTo', $aResult['ReplyTo']);
$aResult['Signature'] = $oSettings->GetConf('Signature', $aResult['Signature']);
$aResult['SignatureToAll'] = !!$oSettings->GetConf('SignatureToAll', $aResult['SignatureToAll']);
$aResult['EnableTwoFactor'] = !!$oSettings->GetConf('EnableTwoFactor', $aResult['EnableTwoFactor']);
$aResult['ParentEmail'] = $oAccount->ParentEmail();
}
@ -1356,27 +1359,36 @@ class Actions
if ($oAccount && $this->TwoFactorAuthProvider()->IsActive())
{
$oSettings = $this->SettingsProvider()->Load($oAccount);
if ($oSettings)
$aData = $this->getTwoFactorInfo($oAccount->ParentEmailHelper());
if ($aData && isset($aData['IsSet'], $aData['Enable']) && !empty($aData['Secret']) && $aData['IsSet'] && $aData['Enable'])
{
$sTwoFactorAuthSecret = $oSettings->GetConf('TwoFactorAuthEnabled', false) ?
$oSettings->GetConf('TwoFactorAuthSecret', '') : '';
if (!empty($sTwoFactorAuthSecret))
$sTwoFactorAuthCode = \trim($sTwoFactorAuthCode);
if (empty($sTwoFactorAuthCode))
{
if (empty($sTwoFactorAuthCode))
$this->Logger()->Write('TwoFactorAuth: Required Code for '.$oAccount->ParentEmailHelper().' account.');
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AccountTwoFactorAuthRequired);
}
else
{
$this->Logger()->Write('TwoFactorAuth: Verify Code for '.$oAccount->ParentEmailHelper().' account.');
$bGood = false;
if (6 < \strlen($sTwoFactorAuthCode) && !empty($aData['BackupCodes']))
{
$this->Logger()->Write('TwoFactorAuth: Required Code for '.$oAccount->Email().' account.');
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AccountTwoFactorAuthRequired);
}
else
{
$this->Logger()->Write('TwoFactorAuth: Verify Code for '.$oAccount->Email().' account.');
if (!$this->TwoFactorAuthProvider()->VerifyCode($sTwoFactorAuthSecret, $sTwoFactorAuthCode))
$aBackupCodes = \explode(' ', \trim(\preg_replace('/[^\d]+/', ' ', $aData['BackupCodes'])));
$bGood = \in_array($sTwoFactorAuthCode, $aBackupCodes);
if ($bGood)
{
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AccountTwoFactorAuthError);
$this->removeBackupCodeFromTwoFactorInfo($oAccount->ParentEmailHelper(), $sTwoFactorAuthCode);
}
}
if (!$bGood && !$this->TwoFactorAuthProvider()->VerifyCode($aData['Secret'], $sTwoFactorAuthCode))
{
$this->loginErrorDelay();
throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AccountTwoFactorAuthError);
}
}
}
}
@ -1420,7 +1432,7 @@ class Actions
$sLanguage = $this->GetActionParam('Language', '');
$bSignMe = '1' === $this->GetActionParam('SignMe', '0');
$sTwoFactorAuthCode = $this->GetActionParam('TwoFactorAuthCode', '');
$sAdditionalCode = $this->GetActionParam('AdditionalCode', '');
$this->Logger()->AddSecret($sPassword);
@ -1430,7 +1442,7 @@ class Actions
{
$oAccount = $this->LoginProcess($sEmail, $sLogin, $sPassword,
$bSignMe ? \md5(\microtime(true).APP_SALT.\rand(10000, 99999).$sEmail) : '',
$sTwoFactorAuthCode);
$sAdditionalCode);
}
catch (\RainLoop\Exceptions\ClientException $oException)
{
@ -2054,6 +2066,7 @@ class Actions
$this->setConfigFromParams($oConfig, 'TokenProtection', 'security', 'csrf_protection', 'bool');
$this->setConfigFromParams($oConfig, 'OpenPGP', 'security', 'openpgp', 'bool');
$this->setConfigFromParams($oConfig, 'AllowTwoFactorAuth', 'security', 'allow_two_factor_auth', 'bool');
$this->setConfigFromParams($oConfig, 'EnabledPlugins', 'plugins', 'enable', 'bool');
$this->setConfigFromParams($oConfig, 'GoogleEnable', 'social', 'google_enable', 'bool');
@ -3199,6 +3212,7 @@ class Actions
$this->setSettingsFromParams($oSettings, 'ReplyTo', 'string');
$this->setSettingsFromParams($oSettings, 'Signature', 'string');
$this->setSettingsFromParams($oSettings, 'SignatureToAll', 'bool');
$this->setSettingsFromParams($oSettings, 'EnableTwoFactor', 'bool');
$this->setSettingsFromParams($oSettings, 'CustomThemeImg', 'string');
@ -4545,6 +4559,236 @@ class Actions
return $this->DefaultResponse(__FUNCTION__, $aQuota);
}
private function getTwoFactorInfo($sEmail, $bRemoveSecret = false)
{
$mData = null;
$aResult = array(
'User' => '',
'IsSet' => false,
'Enable' => false,
'Secret' => '',
'Url' => '',
'BackupCodes' => ''
);
if (!empty($sEmail))
{
$aResult['User'] = $sEmail;
$sData = $this->StorageProvider()->Get(null,
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
'TwoFactorAuth/User/'.$sEmail.'/Data/'
);
if ($sData)
{
$mData = \RainLoop\Utils::DecodeKeyValues($sData);
}
}
if (\is_array($mData) && !empty($aResult['User']) &&
!empty($mData['User']) && !empty($mData['Secret']) &&
!empty($mData['BackupCodes']) && $sEmail === $mData['User'])
{
$aResult['IsSet'] = true;
$aResult['Enable'] = isset($mData['Enable']) ? !!$mData['Enable'] : false;
$aResult['Secret'] = $mData['Secret'];
$aResult['BackupCodes'] = $mData['BackupCodes'];
$aResult['Url'] = $this->TwoFactorAuthProvider()->GetQRCodeGoogleUrl(
$aResult['User'], $aResult['Secret'], $this->Config()->Get('webmail', 'title', ''));
}
if ($bRemoveSecret)
{
if (isset($aResult['Secret']))
{
unset($aResult['Secret']);
}
if (isset($aResult['Url']))
{
unset($aResult['Url']);
}
if (isset($aResult['BackupCodes']))
{
unset($aResult['BackupCodes']);
}
}
return $aResult;
}
/**
* @param string $sEmail
* @param string $sCode
*
* @return bool
*/
private function removeBackupCodeFromTwoFactorInfo($sEmail, $sCode)
{
if (empty($sEmail) || empty($sCode))
{
return false;
}
$sData = $this->StorageProvider()->Get(null,
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
'TwoFactorAuth/User/'.$sEmail.'/Data/'
);
if ($sData)
{
$mData = \RainLoop\Utils::DecodeKeyValues($sData);
if (!empty($mData['BackupCodes']))
{
$sBackupCodes = \preg_replace('/[^\d]+/', ' ', ' '.$mData['BackupCodes'].' ');
$sBackupCodes = \str_replace(' '.$sCode.' ', '', $sBackupCodes);
$mData['BackupCodes'] = \trim(\preg_replace('/[^\d]+/', ' ', $sBackupCodes));
return $this->StorageProvider()->Put(null,
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
'TwoFactorAuth/User/'.$sEmail.'/Data/',
\RainLoop\Utils::EncodeKeyValues($mData)
);
}
}
return false;
}
/**
* @return array
*/
public function DoGetTwoFactorInfo()
{
if (!$this->TwoFactorAuthProvider()->IsActive())
{
return $this->FalseResponse(__FUNCTION__);
}
$oAccount = $this->getAccountFromToken();
return $this->DefaultResponse(__FUNCTION__,
$this->getTwoFactorInfo($oAccount->ParentEmailHelper(), true));
}
/**
* @return array
*/
public function DoCreateTwoFactorSecret()
{
if (!$this->TwoFactorAuthProvider()->IsActive())
{
return $this->FalseResponse(__FUNCTION__);
}
$oAccount = $this->getAccountFromToken();
$sEmail = $oAccount->ParentEmailHelper();
$sSecret = $this->TwoFactorAuthProvider()->CreateSecret();
$aCodes = array();
for ($iIndex = 9; $iIndex > 0; $iIndex--)
{
$aCodes[] = \rand(100000000, 900000000);
}
$this->StorageProvider()->Put(null,
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
'TwoFactorAuth/User/'.$sEmail.'/Data/',
\RainLoop\Utils::EncodeKeyValues(array(
'User' => $sEmail,
'Enable' => false,
'Secret' => $sSecret,
'BackupCodes' => \implode(' ', $aCodes)
))
);
\sleep(1);
return $this->DefaultResponse(__FUNCTION__,
$this->getTwoFactorInfo($sEmail));
}
/**
* @return array
*/
public function DoEnableTwoFactor()
{
if (!$this->TwoFactorAuthProvider()->IsActive())
{
return $this->FalseResponse(__FUNCTION__);
}
$oAccount = $this->getAccountFromToken();
$sEmail = $oAccount->ParentEmailHelper();
$bResult = false;
$mData = $this->getTwoFactorInfo($sEmail);
if (isset($mData['Secret'], $mData['BackupCodes']))
{
$bResult = $this->StorageProvider()->Put(null,
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
'TwoFactorAuth/User/'.$sEmail.'/Data/',
\RainLoop\Utils::EncodeKeyValues(array(
'User' => $sEmail,
'Enable' => '1' === \trim($this->GetActionParam('Enable', '0')),
'Secret' => $mData['Secret'],
'BackupCodes' => $mData['BackupCodes']
))
);
}
return $this->DefaultResponse(__FUNCTION__, $bResult);
}
/**
* @return array
*/
public function DoTestTwoFactorInfo()
{
if (!$this->TwoFactorAuthProvider()->IsActive())
{
return $this->FalseResponse(__FUNCTION__);
}
$oAccount = $this->getAccountFromToken();
$sCode = \trim($this->GetActionParam('Code', ''));
$oData = $this->getTwoFactorInfo($oAccount->ParentEmailHelper());
$sSecret = !empty($oData['Secret']) ? $oData['Secret'] : '';
\sleep(1);
return $this->DefaultResponse(__FUNCTION__,
$this->TwoFactorAuthProvider()->VerifyCode($sSecret, $sCode));
}
/**
* @return array
*/
public function DoClearTwoFactorInfo()
{
if (!$this->TwoFactorAuthProvider()->IsActive())
{
return $this->FalseResponse(__FUNCTION__);
}
$oAccount = $this->getAccountFromToken();
$this->StorageProvider()->Clear(null,
\RainLoop\Providers\Storage\Enumerations\StorageType::NOBODY,
'TwoFactorAuth/User/'.$oAccount->ParentEmailHelper().'/Data/'
);
return $this->DefaultResponse(__FUNCTION__,
$this->getTwoFactorInfo($oAccount->ParentEmailHelper(), true));
}
/**
* @return array
*/

View file

@ -105,6 +105,7 @@ class Application extends \RainLoop\Config\AbstractConfig
'admin_login' => array('admin', 'Login and password for web admin panel'),
'admin_password' => array('12345'),
'allow_admin_panel' => array(true, 'Access settings'),
'allow_two_factor_auth' => array(false),
'admin_panel_host' => array(''),
'core_install_access_domains' => array('')
),

View file

@ -79,6 +79,8 @@ class Notifications
self::SocialGoogleLoginAccessDisable => 'SocialGoogleLoginAccessDisable',
self::DomainNotAllowed => 'DomainNotAllowed',
self::AccountNotAllowed => 'AccountNotAllowed',
self::AccountTwoFactorAuthRequired => 'AccountTwoFactorAuthRequired',
self::AccountTwoFactorAuthError => 'AccountTwoFactorAuthError',
self::CantGetMessageList => 'CantGetMessageList',
self::CantGetMessage => 'CantGetMessage',
self::CantDeleteMessage => 'CantDeleteMessage',

View file

@ -27,9 +27,37 @@ class TwoFactorAuth extends \RainLoop\Providers\AbstractProvider
return $this->oDriver instanceof \RainLoop\Providers\TwoFactorAuth\TwoFactorAuthInterface;
}
/**
* @param string $sName
* @param string $sSecret
* @param string $sTitle = ''
*
* @return string
*/
public function GetQRCodeGoogleUrl($sName, $sSecret, $sTitle = '')
{
$sUrl = sprintf('otpauth://%s/%s?secret=%s&issuer=%s', 'totp', urlencode($sName), $sSecret, urlencode($sTitle));
return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.\urlencode($sUrl);
}
/**
* @return string
*/
public function CreateSecret()
{
$sResult = '';
if ($this->IsActive())
{
$sResult = $this->oDriver->CreateSecret();
}
return $sResult;
}
/**
* @param string $sSecret
* @param string $sCode
*
* @return bool
*/
public function VerifyCode($sSecret, $sCode)

View file

@ -1,206 +1,33 @@
<?php
namespace RainLoop\Providers\TwoFactorAuth;
class GoogleTwoFactorAuth
extends \RainLoop\Providers\TwoFactorAuth\AbstractTwoFactorAuth
implements \RainLoop\Providers\TwoFactorAuth\TwoFactorAuthInterface
{
private $iCodeLength = 6;
/**
* @return string
*/
public function Label()
{
return 'Google Authenticator Code';
}
/**
* Get QR-Code URL for image, from google charts
*
* Function from PHP Class for handling Google Authenticator 2-factor authentication
*
* @author Michael Kliewe
* @copyright 2012 Michael Kliewe
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://www.phpgangsta.de/
*
* @param string $sName
* @param string $sSecret
* @param string $sTitle
* @return string
*/
private function getQRCodeGoogleUrl($sName, $sSecret, $sTitle = null)
{
$sUrlEncoded = \urlencode('otpauth://totp/'.$sName.'?secret='.$sSecret.'');
if(null !== $sTitle)
{
$sUrlEncoded .= \urlencode('&issuer='.$sTitle);
}
return 'https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl='.$sUrlEncoded.'';
}
/**
* Get array with all 32 characters for decoding from/encoding to base32
*
* Function from PHP Class for handling Google Authenticator 2-factor authentication
*
* @author Michael Kliewe
* @copyright 2012 Michael Kliewe
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://www.phpgangsta.de/
*
* @return array
*/
private function getBase32LookupTable()
{
return array(
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
'=' // padding char
);
}
/**
* Helper class to decode base32
*
* Function from PHP Class for handling Google Authenticator 2-factor authentication
*
* @author Michael Kliewe
* @copyright 2012 Michael Kliewe
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://www.phpgangsta.de/
*
* @param $sSecret
*
* @return bool|string
*/
private function base32Decode($sSecret)
{
if (empty($sSecret))
{
return '';
}
$aBase32chars = $this->getBase32LookupTable();
$aBase32charsFlipped = \array_flip($aBase32chars);
$iPaddingCharCount = \substr_count($sSecret, $aBase32chars[32]);
$aAllowedValues = array(6, 4, 3, 1, 0);
if (!\in_array($iPaddingCharCount, $aAllowedValues))
{
return false;
}
for ($iIndex = 0; $iIndex < 4; $iIndex++)
{
if ($iPaddingCharCount === $aAllowedValues[$iIndex] &&
\substr($sSecret, -($aAllowedValues[$iIndex])) !== \str_repeat($aBase32chars[32], $aAllowedValues[$iIndex]))
{
return false;
}
}
$sSecret = \str_replace('=', '', $sSecret);
$sSecret = \str_split($sSecret);
$sBinaryString = '';
for ($iIndex = 0; $iIndex < \count($sSecret); $iIndex = $iIndex + 8)
{
$sX = '';
if (!\in_array($sSecret[$iIndex], $aBase32chars))
{
return false;
}
for ($iJ = 0; $iJ < 8; $iJ++)
{
$sX .= \str_pad(\base_convert(@$aBase32charsFlipped[@$sSecret[$iIndex + $iJ]], 10, 2), 5, '0', STR_PAD_LEFT);
}
$eightBits = \str_split($sX, 8);
for ($iZ = 0; $iZ < \count($eightBits); $iZ++)
{
$sBinaryString .= (($y = \chr(\base_convert($eightBits[$iZ], 2, 10))) || ord($y) == 48 ) ? $y : '';
}
}
return $sBinaryString;
}
/**
* Calculate the code, with given secret and point in time
*
* Function from PHP Class for handling Google Authenticator 2-factor authentication
*
* @author Michael Kliewe
* @copyright 2012 Michael Kliewe
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @link http://www.phpgangsta.de/
*
* @param string $sSecret
* @param int|null $mTimeSlice
*
* @return string
*/
private function getCode($sSecret, $mTimeSlice = null)
{
if (null === $mTimeSlice)
{
$mTimeSlice = \floor(\time() / 30);
}
$sSecretKey = $this->base32Decode($sSecret);
// Pack time into binary string
$sTime = \chr(0).\chr(0).\chr(0).\chr(0).\pack('N*', $mTimeSlice);
// Hash it with users secret key
$sHm = \hash_hmac('SHA1', $sTime, $sSecretKey, true);
// Use last nipple of result as index/offset
$iOffset = \ord(\substr($sHm, -1)) & 0x0F;
// grab 4 bytes of the result
$sHashPart = \substr($sHm, $iOffset, 4);
// Unpak binary value
$sValue = \unpack('N', $sHashPart);
$sValue = $sValue{1};
// Only 32 bits
$sValue = $sValue & 0x7FFFFFFF;
$iMod = \pow(10, $this->iCodeLength);
return \str_pad($sValue % $iMod, $this->iCodeLength, '0', STR_PAD_LEFT);
}
/**
* Check if the code is correct. This will accept codes starting
* from $iDiscrepancy * 30sec ago to $iDiscrepancy * 30sec from now
*
* @param string $sSecret
* @param string $sCode
*
* @return bool
*/
public function VerifyCode($sSecret, $sCode)
{
$iDiscrepancy = 1;
$iTimeSlice = \floor(\time() / 30);
for ($iIndex = -$iDiscrepancy; $iIndex <= $iDiscrepancy; $iIndex++)
{
if ($this->getCode($sSecret, $iTimeSlice + $iIndex) === $sCode)
{
return true;
}
}
return false;
}
}
<?php
namespace RainLoop\Providers\TwoFactorAuth;
class GoogleTwoFactorAuth
extends \RainLoop\Providers\TwoFactorAuth\AbstractTwoFactorAuth
implements \RainLoop\Providers\TwoFactorAuth\TwoFactorAuthInterface
{
/**
* @param string $sSecret
* @param string $sCode
*
* @return bool
*/
public function VerifyCode($sSecret, $sCode)
{
include_once APP_VERSION_ROOT_PATH.'app/libraries/PHPGangsta/GoogleAuthenticator.php';
$oGoogleAuthenticator = new \PHPGangsta_GoogleAuthenticator();
return $oGoogleAuthenticator->verifyCode($sSecret, $sCode);
}
/**
* @return string
*/
public function CreateSecret()
{
include_once APP_VERSION_ROOT_PATH.'app/libraries/PHPGangsta/GoogleAuthenticator.php';
$oGoogleAuthenticator = new \PHPGangsta_GoogleAuthenticator();
return $oGoogleAuthenticator->createSecret();
}
}

View file

@ -1,13 +1,14 @@
<?php
namespace RainLoop\Providers\TwoFactorAuth;
interface TwoFactorAuthInterface
{
/**
* @param string $sSecret
* @param string $sCode
* @return bool
*/
public function VerifyCode($sSecret, $sCode);
}
<?php
namespace RainLoop\Providers\TwoFactorAuth;
interface TwoFactorAuthInterface
{
/**
* @param string $sSecret
* @param string $sCode
*
* @return bool
*/
public function VerifyCode($sSecret, $sCode);
}

View file

@ -17,7 +17,16 @@
<label data-bind="click: function () { openPGP(!openPGP()); }">
<i data-bind="css: openPGP() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
Allow OpenPGP (unstable)
Allow OpenPGP (beta)
</label>
</div>
</div>
<div class="control-group">
<div class="controls">
<label data-bind="click: function () { allowTwoFactorAuth(!allowTwoFactorAuth()); }">
<i data-bind="css: allowTwoFactorAuth() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
Allow 2-Step Verification (Google Authenticator) (beta)
</label>
</div>
</div>

View file

@ -45,6 +45,15 @@
</span>
</div>
</div>
<div class="control-group" data-bind="visible: additionalCode.visibility(), css: {'error': additionalCode.error}">
<div class="input-append">
<input class="i18n inputPassword span4" type="password" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
data-bind="value: additionalCode, hasFocus: additionalCode.focused" data-i18n-placeholder="LOGIN/LABEL_VERIFICATION_CODE" />
<span class="add-on">
<i class="icon-key"></i>
</span>
</div>
</div>
{{INCLUDE/BottomControlGroup/PLACE}}
<div class="control-group">
<button type="submit" class="btn btn-large span4 buttonLogin" data-bind="command: submitCommand">

View file

@ -0,0 +1,32 @@
<div class="popups">
<div class="modal hide b-two-factor-test-content g-ui-user-select-none" data-bind="modal: modalVisibility">
<div>
<div class="modal-header">
<button type="button" class="close" data-bind="command: cancelCommand">&times;</button>
<h3>
<span class="i18n" data-i18n-text="POPUPS_TWO_FACTOR_TEST/TITLE_TEST_CODE"></span>
</h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<br />
<div class="control-group">
<label class="control-label">
<span class="i18n" data-i18n-text="POPUPS_TWO_FACTOR_TEST/LABEL_CODE"></span>
</label>
<div class="controls">
<input class="uiInput inputName" type="text" data-bind="value: code, hasfocus: code.focused, valueUpdate: 'afterkeydown', onEnter: testCode" />
</div>
</div>
</div>
</div>
<div class="modal-footer">
<a class="btn" data-bind="command: testCode, css: { 'btn-success': true === code.status(), 'btn-danger': false === code.status() }">
<i data-bind="css: {'icon-ok': !testing(), 'icon-spinner animated': testing(), 'icon-white': true === code.status() || false === code.status() }"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="POPUPS_TWO_FACTOR_TEST/BUTTON_TEST"></span>
</a>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,82 @@
<div class="b-settings-security">
<div class="form-horizontal">
<div class="legend">
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LEGEND_TWO_FACTOR_AUTH"></span>
</div>
<div class="control-group">
<div class="controls">
<label data-bind="click: function () { viewEnable(!viewEnable()); }">
<i data-bind="css: viewEnable() ? 'icon-checkbox-checked' : 'icon-checkbox-unchecked'"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LABEL_ENABLE_TWO_FACTOR"></span>
</label>
</div>
</div>
<div class="control-group">
<label class="control-label">
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LABEL_TWO_FACTOR_USER"></span>
</label>
<div class="controls" style="padding-top: 5px;">
<strong><span data-bind="text: viewUser"></span></strong>
</div>
</div>
<div class="control-group">
<label class="control-label">
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LABEL_TWO_FACTOR_STATUS"></span>
</label>
<div class="controls" style="padding-top: 5px;">
<strong data-bind="visible: !processing()"><span data-bind="text: viewTwoFactorStatus"></span></strong>
<strong data-bind="visible: processing()">...</strong>
</div>
</div>
<div class="control-group">
<div class="controls">
<a class="btn" data-bind="click: clearTwoFactor, visible: twoFactorStatus">
<i class="icon-remove" data-bind="css: {'icon-remove': !clearing(), 'icon-spinner animated': clearing()}" ></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/BUTTON_CLEAR"></span>
</a>
<span data-bind="visible: twoFactorStatus">&nbsp;&nbsp;</span>
<a class="btn" data-bind="click: createTwoFactor">
<i class="icon-ok" data-bind="css: {'icon-ok': !processing(), 'icon-spinner animated': processing()}" ></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/BUTTON_CREATE"></span>
</a>
<span>&nbsp;&nbsp;</span>
<a class="btn" data-bind="click: testTwoFactor, visible: twoFactorStatus">
<i class="icon-info"></i>
&nbsp;&nbsp;
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/BUTTON_TEST"></span>
</a>
</div>
</div>
<div class="control-group" data-bind="visible: '' !== viewSecret()">
<label class="control-label">
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LABEL_TWO_FACTOR_SECRET"></span>
</label>
<div class="controls" style="padding-top: 5px;">
<strong data-bind="text: viewSecret"></strong>
<br />
<br />
<blockquote>
<p class="muted i18n" style="width: 550px" data-i18n-text="SETTINGS_SECURITY/TWO_FACTOR_SECRET_DESC"></p>
</blockquote>
<!-- ko if: '' !== viewUrl() -->
<img style="margin-left: -7px;" src="javascript:void();" data-bind="attr: {'src': viewUrl}" />
<!-- /ko -->
</div>
</div>
<div class="control-group" data-bind="visible: '' !== viewBackupCodes()">
<label class="control-label">
<span class="i18n" data-i18n-text="SETTINGS_SECURITY/LABEL_TWO_FACTOR_BACKUP_CODES"></span>
</label>
<div class="controls" style="padding-top: 5px;">
<pre data-bind="text: viewBackupCodes" style="width: 230px; word-break: break-word;"></pre>
<br />
<blockquote>
<p class="muted i18n" style="width: 550px" data-i18n-text="SETTINGS_SECURITY/TWO_FACTOR_BACKUP_CODES_DESC"></p>
</blockquote>
</div>
</div>
</div>
</div>

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
LABEL_LOGIN = "Login"
LABEL_PASSWORD = "Passwort"
LABEL_SIGN_ME = "Anmeldung merken"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Anmelden"
TITLE_SIGN_IN_GOOGLE = "Mit Google anmelden"
TITLE_SIGN_IN_FACEBOOK = "Mit Facebook anmelden"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Wählen Sie den System-Ordner aus"
SELECT_CHOOSE_ONE = "Wählen Sie einen aus"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Ordner"
LABEL_ACCOUNTS_NAME = "Konten"
LABEL_IDENTITY_NAME = "Identity"
LABEL_IDENTITIES_NAME = "Identities"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "Vorlagen"
LABEL_CHANGE_PASSWORD_NAME = "Passwort"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reply To"
LABEL_SIGNATURE = "Signatur"
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Allgemein"
LABEL_LANGUAGE = "Sprache"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Diese Social ID ist bisher mit keinem eMa
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Diese Social ID ist bisher mit keinem eMail-Konto verbunden. Melden Sie sich mit Ihren eMail-Daten ein und aktivieren Sie dieses Feature in Ihren Konten-Einstellungen."
DOMAIN_NOT_ALLOWED = "Diese Domain ist nicht zugelassen."
ACCOUNT_NOT_ALLOWED = "Account is not allowed"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Die Nachrichten-Liste ist nicht verfügbar"
CANT_GET_MESSAGE = "Diese Nachricht ist nicht verfügbar"
CANT_DELETE_MESSAGE = "Diese Nachricht kann nicht gelöscht werden"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
LABEL_LOGIN = "Login"
LABEL_PASSWORD = "Password"
LABEL_SIGN_ME = "Remember Me"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Sign In"
TITLE_SIGN_IN_GOOGLE = "Sign In using Google"
TITLE_SIGN_IN_FACEBOOK = "Sign In using Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Select system folders"
SELECT_CHOOSE_ONE = "Choose one"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Folders"
LABEL_ACCOUNTS_NAME = "Accounts"
LABEL_IDENTITY_NAME = "Identity"
LABEL_IDENTITIES_NAME = "Identities"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "Themes"
LABEL_CHANGE_PASSWORD_NAME = "Password"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reply-To"
LABEL_SIGNATURE = "Signature"
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "General"
LABEL_LANGUAGE = "Language"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any em
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any email account yet. Log in using email credentials and add enable this feature in account settings."
DOMAIN_NOT_ALLOWED = "Domain is not allowed"
ACCOUNT_NOT_ALLOWED = "Account is not allowed"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Can't get message list"
CANT_GET_MESSAGE = "Can't get message"
CANT_DELETE_MESSAGE = "Can't delete message"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
LABEL_LOGIN = "Login"
LABEL_PASSWORD = "Contraseña"
LABEL_SIGN_ME = "Recuerdame"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Ingresar"
TITLE_SIGN_IN_GOOGLE = "Ingresar usando Google"
TITLE_SIGN_IN_FACEBOOK = "Ingresar usando Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Seleccione las carpetas del sistema"
SELECT_CHOOSE_ONE = "Elija una"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Carpetas"
LABEL_ACCOUNTS_NAME = "Cuentas"
LABEL_IDENTITY_NAME = "Identity"
LABEL_IDENTITIES_NAME = "Identities"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "Temas"
LABEL_CHANGE_PASSWORD_NAME = "Contraseña"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Responder a"
LABEL_SIGNATURE = "Firma"
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "General"
LABEL_LANGUAGE = "Idioma"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Este ID no está asignado a ninguna cuent
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Este ID no está asignado a ninguna cuenta de email todavía. Inicie sesión con las credenciales de correo electrónico y habilite esta característica en la configuración de cuenta."
DOMAIN_NOT_ALLOWED = "Dominio no permitido"
ACCOUNT_NOT_ALLOWED = "Account is not allowed"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "No se puede obtener la lista de mensajes"
CANT_GET_MESSAGE = "No se puede obtener el mensaje"
CANT_DELETE_MESSAGE = "No se puede eliminar el mensaje"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
LABEL_LOGIN = "Identifiant"
LABEL_PASSWORD = "Mot de passe"
LABEL_SIGN_ME = "Se souvenir de moi"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Se connecter"
TITLE_SIGN_IN_GOOGLE = "Se connecter avec Google"
TITLE_SIGN_IN_FACEBOOK = "Se connecter avec Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Sélectionner les dossiers système"
SELECT_CHOOSE_ONE = "Faites un choix"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Dossiers"
LABEL_ACCOUNTS_NAME = "Comptes"
LABEL_IDENTITY_NAME = "Identité"
LABEL_IDENTITIES_NAME = "Identities"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "Thèmes"
LABEL_CHANGE_PASSWORD_NAME = "Mot de passe"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Répondre à"
LABEL_SIGNATURE = "Signature"
LABEL_ADD_SIGNATURE_TO_ALL = "Ajouter votre signature à tous les messages sortants"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Général"
LABEL_LANGUAGE = "Langue"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Cet ID sociale n'est pas encore assigné
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Cet ID sociale n'est pas encore assigné à une adresse email. Connectez-vous en utilisant les informations d'identification du courrier électronique et activer cette fonctionnalité dans les paramètres de votre compte."
DOMAIN_NOT_ALLOWED = "Ce domaine n'est pas autorisé"
ACCOUNT_NOT_ALLOWED = "Ce compte n'est pas autorisé"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Impossible d'obtenir la liste des messages"
CANT_GET_MESSAGE = "Impossible d'obtenir le message"
CANT_DELETE_MESSAGE = "Impossible de supprimer le message"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "E-mail"
LABEL_LOGIN = "Belépés"
LABEL_PASSWORD = "Jelszó"
LABEL_SIGN_ME = "Megjegyzés"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Belépés"
TITLE_SIGN_IN_GOOGLE = "Belépés Google használatával"
TITLE_SIGN_IN_FACEBOOK = "Belépés Facebook használatával"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Select system folders"
SELECT_CHOOSE_ONE = "Choose one"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Könyvtárak"
LABEL_ACCOUNTS_NAME = "Fiókok"
LABEL_IDENTITY_NAME = "Identitás"
LABEL_IDENTITIES_NAME = "Identitások"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Közösség"
LABEL_THEMES_NAME = "Sablonok"
LABEL_CHANGE_PASSWORD_NAME = "Jelszó"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reply-To"
LABEL_SIGNATURE = "Signature"
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "General"
LABEL_LANGUAGE = "Language"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any em
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any email account yet. Log in using email credentials and add enable this feature in account settings."
DOMAIN_NOT_ALLOWED = "Domain is not allowed"
ACCOUNT_NOT_ALLOWED = "Account is not allowed"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Can't get message list"
CANT_GET_MESSAGE = "Can't get message"
CANT_DELETE_MESSAGE = "Can't delete message"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Netfang"
LABEL_LOGIN = "Innskrá"
LABEL_PASSWORD = "Lykilorð"
LABEL_SIGN_ME = "Muna"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Innskrá"
TITLE_SIGN_IN_GOOGLE = "Innskráning með Google"
TITLE_SIGN_IN_FACEBOOK = "Innskráning með Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Veldu kerfismöppur"
SELECT_CHOOSE_ONE = "Veldu eitt"
@ -321,7 +327,8 @@ LABEL_FOLDERS_NAME = "Möppur"
LABEL_ACCOUNTS_NAME = "Aðgangar"
LABEL_IDENTITY_NAME = "Identity"
LABEL_IDENTITIES_NAME = "Identities"
LABEL_SOCIAL_NAME = "Félagsmiðlar"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAMELABEL_SOCIAL_NAME = "Félagsmiðlar"
LABEL_THEMES_NAME = "Þemur"
LABEL_CHANGE_PASSWORD_NAME = "Lykilorð"
LABEL_OPEN_PGP_NAME = "OpenPGP"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Svara"
LABEL_SIGNATURE = "Undirskrift"
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Almennt"
LABEL_LANGUAGE = "Tungumál"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Þetta samfélags ID er ekki úthlutað
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Þetta samfélags ID er ekki úthlutað á neitt netfang ennþá. Skráðu þig inn með netfangs upplýsingum og bættu þessum valmöguleika við í aðgangsstillingum."
DOMAIN_NOT_ALLOWED = "Lén ekki leyft"
ACCOUNT_NOT_ALLOWED = "Account is not allowed"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Get ekki sótt bréfa lista"
CANT_GET_MESSAGE = "Get ekki sótt bréf"
CANT_DELETE_MESSAGE = "Get ekki eytt bréfi"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
LABEL_LOGIN = "Login"
LABEL_PASSWORD = "Password"
LABEL_SIGN_ME = "Ricordami"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Accedi"
TITLE_SIGN_IN_GOOGLE = "Accedi usando Google"
TITLE_SIGN_IN_FACEBOOK = "Accedi usando Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Seleziona le cartelle di sistema"
SELECT_CHOOSE_ONE = "Selezionane una"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Cartelle"
LABEL_ACCOUNTS_NAME = "Account"
LABEL_IDENTITY_NAME = "Identità"
LABEL_IDENTITIES_NAME = "Identità"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "Temi"
LABEL_CHANGE_PASSWORD_NAME = "Password"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Rispondi a"
LABEL_SIGNATURE = "Firma"
LABEL_ADD_SIGNATURE_TO_ALL = "Aggiungi la firma a tutti i messaggi in uscita"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Generali"
LABEL_LANGUAGE = "Lingua"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Questo ID sociale non è collegato ad alc
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Questo ID sociale non è collegato ad alcun indirizzo email. Entra utilizzando le credenziali delle email e abilita questa funzionalità nelle impostazioni."
DOMAIN_NOT_ALLOWED = "Il dominio non è autorizzato"
ACCOUNT_NOT_ALLOWED = "L'account non è autorizzato"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Impossibile ottenere la lista dei messaggi"
CANT_GET_MESSAGE = "Impossibile ottenere il messaggio"
CANT_DELETE_MESSAGE = "Impossibile cancellare il messaggio"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "メールアドレス"
LABEL_LOGIN = "ログイン"
LABEL_PASSWORD = "パスワード"
LABEL_SIGN_ME = "サインイン状態を保持する"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "サインイン"
TITLE_SIGN_IN_GOOGLE = "Googleアカウントでログイン"
TITLE_SIGN_IN_FACEBOOK = "Facebookアカウントでログイン"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Select system folders"
SELECT_CHOOSE_ONE = "Choose one"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "フォルダ"
LABEL_ACCOUNTS_NAME = "アカウント"
LABEL_IDENTITY_NAME = "Identity"
LABEL_IDENTITIES_NAME = "表示名"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "テーマ"
LABEL_CHANGE_PASSWORD_NAME = "Password"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reply-To"
LABEL_SIGNATURE = "署名"
LABEL_ADD_SIGNATURE_TO_ALL = "すべての送信メッセージに署名を追加する"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "全般"
LABEL_LANGUAGE = "言語"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any em
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any email account yet. Log in using email credentials and add enable this feature in account settings."
DOMAIN_NOT_ALLOWED = "Domain is not allowed"
ACCOUNT_NOT_ALLOWED = "Account is not allowed"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Can't get message list"
CANT_GET_MESSAGE = "Can't get message"
CANT_DELETE_MESSAGE = "Can't delete message"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "이메일 주소"
LABEL_LOGIN = "아이디"
LABEL_PASSWORD = "비밀번호"
LABEL_SIGN_ME = "아이디 기억"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "로그인"
TITLE_SIGN_IN_GOOGLE = "구글 계정으로 로그인"
TITLE_SIGN_IN_FACEBOOK = "페이스북 계정으로 로그인"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "메일함을 지정해주세요"
SELECT_CHOOSE_ONE = "선택해주세요"
@ -318,6 +324,7 @@ LABEL_FOLDERS_NAME = "메일함"
LABEL_ACCOUNTS_NAME = "계정"
LABEL_IDENTITY_NAME = "신원"
LABEL_IDENTITIES_NAME = "신원"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "소셜"
LABEL_THEMES_NAME = "테마"
LABEL_CHANGE_PASSWORD_NAME = "비밀번호"
@ -331,6 +338,21 @@ LABEL_REPLY_TO = "답장"
LABEL_SIGNATURE = "서명"
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "일반"
LABEL_LANGUAGE = "언어"
@ -449,6 +471,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "이 트위터 아이디는 아직 어떠
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "이 구글 아이디는 아직 어떠한 이메일 계정에도 연결되지 않았습니다. 이메일 계정으로 로그인 하신 후 소셜 설정에서 소셜 계정에 연결해주세요."
DOMAIN_NOT_ALLOWED = "허용된 도메인이 아닙니다."
ACCOUNT_NOT_ALLOWED = "허용된 계정이 아닙니다."
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "메시지 목록을 불러 올 수 없습니다."
CANT_GET_MESSAGE = "메시지를 가져올 수 없습니다."
CANT_DELETE_MESSAGE = "메시지를 삭제할 수 없습니다."

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "E-pasts"
LABEL_LOGIN = "Ielogoties"
LABEL_PASSWORD = "Parole"
LABEL_SIGN_ME = "Atcerēties mani"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Ielogoties"
TITLE_SIGN_IN_GOOGLE = "Ielogoties izmantojot Google"
TITLE_SIGN_IN_FACEBOOK = "Ielogoties izmantojot Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Izvēlaties sistēmas mapes"
SELECT_CHOOSE_ONE = "Izvēlaties vienu"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Mapes"
LABEL_ACCOUNTS_NAME = "Konti"
LABEL_IDENTITY_NAME = "Identity"
LABEL_IDENTITIES_NAME = "Identities"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Sociālie"
LABEL_THEMES_NAME = "Tēmas"
LABEL_CHANGE_PASSWORD_NAME = "Parole"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Atbildēt uz"
LABEL_SIGNATURE = "Paraksts"
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Vispārējie"
LABEL_LANGUAGE = "Valoda"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Sociālā ID nav pievienot nevienam e-pas
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Sociālā ID nav pievienot nevienam e-pastam. Ielogojaties ar e-pastu un ieslēdzat šādu iespeju uzstādijumos."
DOMAIN_NOT_ALLOWED = "Šis domēns nav atļauts"
ACCOUNT_NOT_ALLOWED = "Account is not allowed"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Nevar ielādēt ziņojumu sarakstu"
CANT_GET_MESSAGE = "Nevar ielādēt ziņojumu"
CANT_DELETE_MESSAGE = "Nevar izdzēst ziņojumu"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
LABEL_LOGIN = "Login"
LABEL_PASSWORD = "Paswoord"
LABEL_SIGN_ME = "Aangemeld blijven"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Aanmelden"
TITLE_SIGN_IN_GOOGLE = "Aanmelden met Google"
TITLE_SIGN_IN_FACEBOOK = "Aanmelden met Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Selecteer systeem mappen"
SELECT_CHOOSE_ONE = "Kies één"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Mappen"
LABEL_ACCOUNTS_NAME = "Accounts"
LABEL_IDENTITY_NAME = "Identity"
LABEL_IDENTITIES_NAME = "Identities"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "Themes"
LABEL_CHANGE_PASSWORD_NAME = "Paswoord"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Antwoorden naar"
LABEL_SIGNATURE = "Handtekening"
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Algemeen"
LABEL_LANGUAGE = "Taal"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any em
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any email account yet. Log in using email credentials and add enable this feature in account settings."
DOMAIN_NOT_ALLOWED = "domein is niet toegestaan"
ACCOUNT_NOT_ALLOWED = "Account is niet toegestaan"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Can't get message list"
CANT_GET_MESSAGE = "Can't get message"
CANT_DELETE_MESSAGE = "Kan bericht niet verwijderen"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Epost"
LABEL_LOGIN = "Logg inn"
LABEL_PASSWORD = "Passord"
LABEL_SIGN_ME = "Husk meg"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Logg Inn"
TITLE_SIGN_IN_GOOGLE = "Logg inn Med Google"
TITLE_SIGN_IN_FACEBOOK = "Logg inn MED Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Velg systemmapper"
SELECT_CHOOSE_ONE = "Velg en"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Mapper"
LABEL_ACCOUNTS_NAME = "Kontoer"
LABEL_IDENTITY_NAME = "Identitet"
LABEL_IDENTITIES_NAME = "identiteter"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Sosialt"
LABEL_THEMES_NAME = "Tema"
LABEL_CHANGE_PASSWORD_NAME = "Passord"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Svar til"
LABEL_SIGNATURE = "Signatur"
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Generelt"
LABEL_LANGUAGE = "Språk"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Denne sosiale ID er ikke tildelt for alle
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Denne sosiale ID er ikke tildelt for alle e-post -konto ennå. Logg inn med e- legitimasjon og legge aktivere denne funksjonen i kontoinnstillingene ."
DOMAIN_NOT_ALLOWED = "Domene er ikke tillatt"
ACCOUNT_NOT_ALLOWED = "Kontoen er ikke tillatt"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Kan ikke få meldingsliste"
CANT_GET_MESSAGE = "Kan ikke få meldingen"
CANT_DELETE_MESSAGE = "Kan ikke slette meldingen"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Login/Email"
LABEL_LOGIN = "Login"
LABEL_PASSWORD = "Hasło"
LABEL_SIGN_ME = "Zapamiętaj mnie"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Zaloguj"
TITLE_SIGN_IN_GOOGLE = "Zaloguj się przez Google"
TITLE_SIGN_IN_FACEBOOK = "Zaloguj się przez Facebook"
@ -266,6 +267,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Wybierz foldery systemowe"
SELECT_CHOOSE_ONE = "Wybierz"
@ -320,7 +326,8 @@ LABEL_FOLDERS_NAME = "Foldery"
LABEL_ACCOUNTS_NAME = "Konta"
LABEL_IDENTITY_NAME = "Tożsamość"
LABEL_IDENTITIES_NAME = "Tożsamości"
LABEL_SOCIAL_NAME = "Social Media"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "Wygląd"
LABEL_CHANGE_PASSWORD_NAME = "Hasło"
LABEL_OPEN_PGP_NAME = "OpenPGP"
@ -333,6 +340,21 @@ LABEL_REPLY_TO = "Zwrotny adres e-mail"
LABEL_SIGNATURE = "Podpis"
LABEL_ADD_SIGNATURE_TO_ALL = "Dadawaj podpis do wszystkich wysyłanych wiadomości"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Ogólne"
LABEL_LANGUAGE = "Język"
@ -451,6 +473,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "To społecznościowe ID na Twitter nie je
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "To społecznościowe ID (Google Acounts) nie jest powiązane z żadnym kontem pocztowym. W związku z tym zaloguj się używając danych swojego konta e-mail i skonfiguruj tę opcję w Ustawieniach > Social Media"
DOMAIN_NOT_ALLOWED = "Domena nie dozwolona"
ACCOUNT_NOT_ALLOWED = "Konto nie jest dozwolone"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Nie jest możliwe pobranie listy wiadomości"
CANT_GET_MESSAGE = "Nie można pobrać wiadomości"
CANT_DELETE_MESSAGE = "Nie można usunąć wiadomości"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
LABEL_LOGIN = "Usuário"
LABEL_PASSWORD = "Senha"
LABEL_SIGN_ME = "Lembre-me"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Entre"
TITLE_SIGN_IN_GOOGLE = "Entre com a conta do Google"
TITLE_SIGN_IN_FACEBOOK = "Entre com a conta do Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Selecione as pastas do sistema"
SELECT_CHOOSE_ONE = "Escolha um"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Pastas"
LABEL_ACCOUNTS_NAME = "Contas"
LABEL_IDENTITY_NAME = "Identidade"
LABEL_IDENTITIES_NAME = "Identidades"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "Temas"
LABEL_CHANGE_PASSWORD_NAME = "Senha"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reenviar para"
LABEL_SIGNATURE = "Assinatura"
LABEL_ADD_SIGNATURE_TO_ALL = "Adicionar a sua assinatura a todas as mensagens de saída"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Geral"
LABEL_LANGUAGE = "Linguagem"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Esta ID social não é atribuído para qu
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Esta ID social não é atribuído para qualquer conta de e-mail ainda. Entrar usando credenciais de e-mail e adicione ativar esse recurso em configurações de conta."
DOMAIN_NOT_ALLOWED = "Este domínio não é permitido"
ACCOUNT_NOT_ALLOWED = "Account is not allowed"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Não é possível obter a lista de mensagens"
CANT_GET_MESSAGE = "Não é possível obter a mensagem"
CANT_DELETE_MESSAGE = "Não é possível excluir a mensagem"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
LABEL_LOGIN = "Usuário"
LABEL_PASSWORD = "Senha"
LABEL_SIGN_ME = "Lembre-me"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Entre"
TITLE_SIGN_IN_GOOGLE = "Entre com a conta do Google"
TITLE_SIGN_IN_FACEBOOK = "Entre com a conta do Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Selecione as pastas do sistema"
SELECT_CHOOSE_ONE = "Escolha um"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Pastas"
LABEL_ACCOUNTS_NAME = "Contas"
LABEL_IDENTITY_NAME = "Identidade"
LABEL_IDENTITIES_NAME = "Identidades"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "Temas"
LABEL_CHANGE_PASSWORD_NAME = "Senha"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Reenviar para"
LABEL_SIGNATURE = "Assinatura"
LABEL_ADD_SIGNATURE_TO_ALL = "Adicionar a sua assinatura a todas as mensagens de saída"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Geral"
LABEL_LANGUAGE = "Linguagem"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Esta ID social não é atribuído para qu
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Esta ID social não é atribuído para qualquer conta de e-mail ainda. Entrar usando credenciais de e-mail e adicione ativar esse recurso em configurações de conta."
DOMAIN_NOT_ALLOWED = "Este domínio não é permitido"
ACCOUNT_NOT_ALLOWED = "Account is not allowed"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Não é possível obter a lista de mensagens"
CANT_GET_MESSAGE = "Não é possível obter a mensagem"
CANT_DELETE_MESSAGE = "Não é possível excluir a mensagem"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Email"
LABEL_LOGIN = "Conectare"
LABEL_PASSWORD = "Parolă"
LABEL_SIGN_ME = "Ține-mă minte"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "Conectare"
TITLE_SIGN_IN_GOOGLE = "Войти, используя Google"
TITLE_SIGN_IN_FACEBOOK = "Войти, используя Facebook"
@ -266,6 +267,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Selectați dosarul de sistem"
SELECT_CHOOSE_ONE = "Nu a fost încă selectat"
@ -320,6 +326,7 @@ LABEL_FOLDERS_NAME = "Dosare"
LABEL_ACCOUNTS_NAME = "Conturi"
LABEL_IDENTITY_NAME = "Profil"
LABEL_IDENTITIES_NAME = "Profiluri"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "Subiecte"
LABEL_CHANGE_PASSWORD_NAME = "Parolă"
@ -333,6 +340,21 @@ LABEL_REPLY_TO = "Răspunde la"
LABEL_SIGNATURE = "Semnătură"
LABEL_ADD_SIGNATURE_TO_ALL = "Adăugă semnătura la toate mesajele trimise"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Setări de bază"
LABEL_LANGUAGE = "Limbă"
@ -451,6 +473,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "Pentru acest utilizator nu sa asigurat î
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "Pentru acest utilizator nu sa asigurat încă un cont de e-mail. Conectați-vă cu contul dvs. de e-mail și activați această caracteristică în setările."
DOMAIN_NOT_ALLOWED = "Domeniul nu apartine de Wey5"
ACCOUNT_NOT_ALLOWED = "Contul nu are permisiunea de conectare"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "Nu găsesc o lista de scrisori"
CANT_GET_MESSAGE = "Nu pot obține scrisoarea. Încercați din nou"
CANT_DELETE_MESSAGE = "Nu pot șterge scrisoarea. Încercați din nou"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "Почта"
LABEL_LOGIN = "Логин"
LABEL_PASSWORD = "Пароль"
LABEL_SIGN_ME = "Запомнить меня"
LABEL_VERIFICATION_CODE = "Код подтверждения"
BUTTON_SIGN_IN = "Войти"
TITLE_SIGN_IN_GOOGLE = "Войти, используя Google"
TITLE_SIGN_IN_FACEBOOK = "Войти, используя Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Подпись"
BUTTON_ENCRYPT = "Шифрование"
BUTTON_SIGN_AND_ENCRYPT = "Подпись и шифрование"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "Тест двухфакторной верификации"
LABEL_CODE = "Код"
BUTTON_TEST = "Тест"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "Выберите системые папки"
SELECT_CHOOSE_ONE = "Еще не выбрана"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "Папки"
LABEL_ACCOUNTS_NAME = "Аккаунты"
LABEL_IDENTITY_NAME = "Профиль"
LABEL_IDENTITIES_NAME = "Профили"
LABEL_SECURITY_NAME = "Безопасность"
LABEL_SOCIAL_NAME = "Социальные"
LABEL_THEMES_NAME = "Темы"
LABEL_CHANGE_PASSWORD_NAME = "Пароль"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "Отвечать на"
LABEL_SIGNATURE = "Подпись"
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "Двухфакторная верификация (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Включить двухфакторную верификацию"
LABEL_TWO_FACTOR_USER = "Пользователь"
LABEL_TWO_FACTOR_STATUS = "Статус"
LABEL_TWO_FACTOR_SECRET = "Секретный ключ"
LABEL_TWO_FACTOR_BACKUP_CODES = "Резервные коды"
BUTTON_CREATE = "Создать новый секретный ключ"
BUTTON_CLEAR = "Очистить"
BUTTON_TEST = "Тест"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Настроен"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Не настроен"
TWO_FACTOR_SECRET_DESC = "Используйте эту информацию для настройки Gooogle Authenticator клиента (или другого TOTP клиента) с помощью прилагаемого QR-кода, или вручную."
TWO_FACTOR_BACKUP_CODES_DESC = "Если вы не можете получить коды через Google Authenticator, вы можете использовать резервные коды, чтобы войти. После того как вы использовали резервный код для входа в систему, он становится неактивным."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "Основные Настройки"
LABEL_LANGUAGE = "Язык"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "К данному социальному
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "К данному социальному пользователю еще не прикреплен почтовый аккаунт. Войдите в систему под своим почтовым аккаунтом и включите эту возможность в настройках."
DOMAIN_NOT_ALLOWED = "Данный домен не разрешен"
ACCOUNT_NOT_ALLOWED = "Данный аккаунт не разрешен"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Необходима двухфакторная верификация"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Ошибка двухфакторной верификации"
CANT_GET_MESSAGE_LIST = "Не могу получить список писем"
CANT_GET_MESSAGE = "Не могу получить письмо"
CANT_DELETE_MESSAGE = "Не могу удалить письмо"

View file

@ -3,6 +3,7 @@ LABEL_EMAIL = "邮件"
LABEL_LOGIN = "登陆"
LABEL_PASSWORD = "密码"
LABEL_SIGN_ME = "记住我"
LABEL_VERIFICATION_CODE = "Verification Code"
BUTTON_SIGN_IN = "登入"
TITLE_SIGN_IN_GOOGLE = "Sign In using Google"
TITLE_SIGN_IN_FACEBOOK = "Sign In using Facebook"
@ -267,6 +268,11 @@ BUTTON_SIGN = "Sign"
BUTTON_ENCRYPT = "Encrypt"
BUTTON_SIGN_AND_ENCRYPT = "Sign and encrypt"
[POPUPS_TWO_FACTOR_TEST]
TITLE_TEST_CODE = "2-Step verification test"
LABEL_CODE = "Code"
BUTTON_TEST = "Test"
[POPUPS_SYSTEM_FOLDERS]
TITLE_SYSTEM_FOLDERS = "选择系统文件夹"
SELECT_CHOOSE_ONE = "选择一个"
@ -321,6 +327,7 @@ LABEL_FOLDERS_NAME = "文件夹"
LABEL_ACCOUNTS_NAME = "账户"
LABEL_IDENTITY_NAME = "签名"
LABEL_IDENTITIES_NAME = "签名"
LABEL_SECURITY_NAME = "Security"
LABEL_SOCIAL_NAME = "Social"
LABEL_THEMES_NAME = "主题"
LABEL_CHANGE_PASSWORD_NAME = "密码"
@ -334,6 +341,21 @@ LABEL_REPLY_TO = "回复"
LABEL_SIGNATURE = "签名"
LABEL_ADD_SIGNATURE_TO_ALL = "Add your signature to all the outgoing messages"
[SETTINGS_SECURITY]
LEGEND_TWO_FACTOR_AUTH = "2-Step Verification (Google Authenticator)"
LABEL_ENABLE_TWO_FACTOR = "Enable 2-Step verification"
LABEL_TWO_FACTOR_USER = "User"
LABEL_TWO_FACTOR_STATUS = "Status"
LABEL_TWO_FACTOR_SECRET = "Secret"
LABEL_TWO_FACTOR_BACKUP_CODES = "Backup codes"
BUTTON_CREATE = "Create New Secret"
BUTTON_CLEAR = "Clear"
BUTTON_TEST = "Test"
TWO_FACTOR_SECRET_CONFIGURED_DESC = "Configured"
TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC = "Not configured"
TWO_FACTOR_SECRET_DESC = "Import this info into your Gooogle Authenticator client (or other TOTP client) using the provided QR code below or by entering the code manually."
TWO_FACTOR_BACKUP_CODES_DESC = "If you can't receive codes via Google Authenticator, you can use backup codes to sign in. After youve used a backup code to sign in, it will become inactive."
[SETTINGS_GENERAL]
LEGEND_GENERAL = "一般设置"
LABEL_LANGUAGE = "语言"
@ -452,6 +474,8 @@ SOCIAL_TWITTER_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any em
SOCIAL_GOOGLE_LOGIN_ACCESS_DISABLE = "This social ID is not assigned for any email account yet. Log in using email credentials and add enable this feature in account settings."
DOMAIN_NOT_ALLOWED = "域不允许"
ACCOUNT_NOT_ALLOWED = "账户不允许"
ACCOUNT_TWO_FACTOR_AUTH_REQUIRED = "Two factor verification required"
ACCOUNT_TWO_FACTOR_AUTH_ERROR = "Two factor verification error"
CANT_GET_MESSAGE_LIST = "无法获取邮件列表"
CANT_GET_MESSAGE = "无法获取邮件"
CANT_DELETE_MESSAGE = "无法删除邮件"

View file

@ -6607,6 +6607,9 @@ html.rl-no-preview-pane #rl-right .ui-resizable-handle {
max-height: 500px;
overflow: auto;
}
.popups .b-two-factor-test-content .modal-header {
background-color: #fff;
}
.popups .b-identity-content .modal-header {
background-color: #fff;
}

File diff suppressed because one or more lines are too long

View file

@ -657,6 +657,9 @@ Enums.Notification = {
'DomainNotAllowed': 109,
'AccountNotAllowed': 110,
'AccountTwoFactorAuthRequired': 120,
'AccountTwoFactorAuthError': 121,
'CantGetMessageList': 201,
'CantGetMessage': 202,
'CantDeleteMessage': 203,
@ -1266,6 +1269,9 @@ Utils.initNotificationLanguage = function ()
NotificationI18N[Enums.Notification.DomainNotAllowed] = Utils.i18n('NOTIFICATIONS/DOMAIN_NOT_ALLOWED');
NotificationI18N[Enums.Notification.AccountNotAllowed] = Utils.i18n('NOTIFICATIONS/ACCOUNT_NOT_ALLOWED');
NotificationI18N[Enums.Notification.AccountTwoFactorAuthRequired] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_REQUIRED');
NotificationI18N[Enums.Notification.AccountTwoFactorAuthError] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_ERROR');
NotificationI18N[Enums.Notification.CantGetMessageList] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST');
NotificationI18N[Enums.Notification.CantGetMessage] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE');
NotificationI18N[Enums.Notification.CantDeleteMessage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_MESSAGE');
@ -1370,14 +1376,14 @@ Utils.delegateRun = function (oObject, sMethodName, aParameters, nDelay)
Utils.killCtrlAandS = function (oEvent)
{
oEvent = oEvent || window.event;
if (oEvent)
if (oEvent && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey)
{
var
oSender = oEvent.target || oEvent.srcElement,
iKey = oEvent.keyCode || oEvent.which
;
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.S)
if (iKey === Enums.EventKeyCode.S)
{
oEvent.preventDefault();
return;
@ -1388,7 +1394,7 @@ Utils.killCtrlAandS = function (oEvent)
return;
}
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.A)
if (iKey === Enums.EventKeyCode.A)
{
if (window.getSelection)
{
@ -2415,51 +2421,6 @@ Utils.selectElement = function (element)
/* jshint onevar: true */
};
Utils.openPgpImportPublicKeys = function (sPublicKeysArmored)
{
if (window.openpgp && RL)
{
var
oOpenpgpKeyring = RL.data().openpgpKeyring,
oImported = window.openpgp.key.readArmored(sPublicKeysArmored)
;
if (oOpenpgpKeyring && oImported && !oImported.err && Utils.isArray(oImported.keys) &&
0 < oImported.keys.length)
{
_.each(oImported.keys, function (oPrivKey) {
if (oPrivKey)
{
window.console.log(oPrivKey);
// var oKey = oOpenpgpKeyring.getKeysForKeyId(oPrivKey.primaryKey.getFingerprint());
// if (oKey && oKey[0])
// {
// if (oKey[0].isPublic())
// {
// oPrivKey.update(oKey[0]);
// oOpenpgpKeyring.publicKeys.removeForId(oPrivKey.primaryKey.getFingerprint());
// oOpenpgpKeyring.privateKeys.push(oPrivKey);
// }
// else
// {
// oKey[0].update(oPrivKey);
// }
// }
// else
// {
// oOpenpgpKeyring.importKey(oPrivKey.armored);
// }
}
});
oOpenpgpKeyring.store();
return true;
}
}
return false;
};
// Base64 encode / decode
// http://www.webtoolkit.info/
@ -6087,6 +6048,7 @@ function AdminSecurity()
{
this.csrfProtection = ko.observable(!!RL.settingsGet('UseTokenProtection'));
this.openPGP = ko.observable(!!RL.settingsGet('OpenPGP'));
this.allowTwoFactorAuth = ko.observable(!!RL.settingsGet('AllowTwoFactorAuth'));
this.adminLogin = ko.observable(RL.settingsGet('AdminLogin'));
this.adminPassword = ko.observable('');
@ -6152,6 +6114,12 @@ AdminSecurity.prototype.onBuild = function ()
'OpenPGP': bValue ? '1' : '0'
});
});
this.allowTwoFactorAuth.subscribe(function (bValue) {
RL.remote().saveAdminConfig(Utils.emptyFunction, {
'AllowTwoFactorAuth': bValue ? '1' : '0'
});
});
};
AdminSecurity.prototype.onHide = function ()

File diff suppressed because one or more lines are too long

View file

@ -661,6 +661,9 @@ Enums.Notification = {
'DomainNotAllowed': 109,
'AccountNotAllowed': 110,
'AccountTwoFactorAuthRequired': 120,
'AccountTwoFactorAuthError': 121,
'CantGetMessageList': 201,
'CantGetMessage': 202,
'CantDeleteMessage': 203,
@ -1270,6 +1273,9 @@ Utils.initNotificationLanguage = function ()
NotificationI18N[Enums.Notification.DomainNotAllowed] = Utils.i18n('NOTIFICATIONS/DOMAIN_NOT_ALLOWED');
NotificationI18N[Enums.Notification.AccountNotAllowed] = Utils.i18n('NOTIFICATIONS/ACCOUNT_NOT_ALLOWED');
NotificationI18N[Enums.Notification.AccountTwoFactorAuthRequired] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_REQUIRED');
NotificationI18N[Enums.Notification.AccountTwoFactorAuthError] = Utils.i18n('NOTIFICATIONS/ACCOUNT_TWO_FACTOR_AUTH_ERROR');
NotificationI18N[Enums.Notification.CantGetMessageList] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE_LIST');
NotificationI18N[Enums.Notification.CantGetMessage] = Utils.i18n('NOTIFICATIONS/CANT_GET_MESSAGE');
NotificationI18N[Enums.Notification.CantDeleteMessage] = Utils.i18n('NOTIFICATIONS/CANT_DELETE_MESSAGE');
@ -1374,14 +1380,14 @@ Utils.delegateRun = function (oObject, sMethodName, aParameters, nDelay)
Utils.killCtrlAandS = function (oEvent)
{
oEvent = oEvent || window.event;
if (oEvent)
if (oEvent && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey)
{
var
oSender = oEvent.target || oEvent.srcElement,
iKey = oEvent.keyCode || oEvent.which
;
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.S)
if (iKey === Enums.EventKeyCode.S)
{
oEvent.preventDefault();
return;
@ -1392,7 +1398,7 @@ Utils.killCtrlAandS = function (oEvent)
return;
}
if (oEvent.ctrlKey && iKey === Enums.EventKeyCode.A)
if (iKey === Enums.EventKeyCode.A)
{
if (window.getSelection)
{
@ -2419,51 +2425,6 @@ Utils.selectElement = function (element)
/* jshint onevar: true */
};
Utils.openPgpImportPublicKeys = function (sPublicKeysArmored)
{
if (window.openpgp && RL)
{
var
oOpenpgpKeyring = RL.data().openpgpKeyring,
oImported = window.openpgp.key.readArmored(sPublicKeysArmored)
;
if (oOpenpgpKeyring && oImported && !oImported.err && Utils.isArray(oImported.keys) &&
0 < oImported.keys.length)
{
_.each(oImported.keys, function (oPrivKey) {
if (oPrivKey)
{
window.console.log(oPrivKey);
// var oKey = oOpenpgpKeyring.getKeysForKeyId(oPrivKey.primaryKey.getFingerprint());
// if (oKey && oKey[0])
// {
// if (oKey[0].isPublic())
// {
// oPrivKey.update(oKey[0]);
// oOpenpgpKeyring.publicKeys.removeForId(oPrivKey.primaryKey.getFingerprint());
// oOpenpgpKeyring.privateKeys.push(oPrivKey);
// }
// else
// {
// oKey[0].update(oPrivKey);
// }
// }
// else
// {
// oOpenpgpKeyring.importKey(oPrivKey.armored);
// }
}
});
oOpenpgpKeyring.store();
return true;
}
}
return false;
};
// Base64 encode / decode
// http://www.webtoolkit.info/
@ -3841,7 +3802,7 @@ NewHtmlEditorWrapper.prototype.init = function ()
{
self.editor.on('instanceReady', function () {
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65/* A */, 'selectAll');
self.editor.setKeystroke(window.CKEDITOR.CTRL + 65 /* A */, 'selectAll');
self.fOnReady();
self.__resizable = true;
@ -7355,15 +7316,14 @@ FolderModel.prototype.initComputed = function ()
{
return '' + iCount;
}
else if (0 < iUnread && Enums.FolderType.Trash !== iType && Enums.FolderType.SentItems !== iType)
else if (0 < iUnread && Enums.FolderType.Trash !== iType && Enums.FolderType.Archive !== iType && Enums.FolderType.SentItems !== iType)
{
return '' + iUnread;
}
}
return '';
// return 0 < iUnread && (Enums.FolderType.Inbox === iType || Enums.FolderType.Spam === iType) ? '' + iUnread :
// (0 < iCount && Enums.FolderType.Draft === iType ? '' + iCount : '');
}, this);
this.canBeDeleted = ko.computed(function () {
@ -8880,12 +8840,12 @@ PopupsComposeViewModel.prototype.onBuild = function ()
if (oEvent && self.modalVisibility() && RL.data().useKeyboardShortcuts())
{
if (self.bAllowCtrlS && oEvent.ctrlKey && Enums.EventKeyCode.S === oEvent.keyCode)
if (self.bAllowCtrlS && oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.S === oEvent.keyCode)
{
self.saveCommand();
bResult = false;
}
else if (oEvent.ctrlKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
else if (oEvent.ctrlKey && !oEvent.shiftKey && !oEvent.altKey && Enums.EventKeyCode.Enter === oEvent.keyCode)
{
self.sendCommand();
bResult = false;
@ -10904,6 +10864,60 @@ PopupsLanguagesViewModel.prototype.changeLanguage = function (sLang)
this.cancelCommand();
};
/**
* @constructor
* @extends KnoinAbstractViewModel
*/
function PopupsTwoFactorTestViewModel()
{
KnoinAbstractViewModel.call(this, 'Popups', 'PopupsTwoFactorTest');
var self = this;
this.code = ko.observable('');
this.code.focused = ko.observable(false);
this.code.status = ko.observable(null);
this.testing = ko.observable(false);
// commands
this.testCode = Utils.createCommand(this, function () {
this.testing(true);
RL.remote().testTwoFactor(function (sResult, oData) {
self.testing(false);
self.code.status(Enums.StorageResultType.Success === sResult && oData && oData.Result ? true : false);
}, this.code());
}, function () {
return '' !== this.code() && !this.testing();
});
Knoin.constructorEnd(this);
}
Utils.extendAsViewModel('PopupsTwoFactorTestViewModel', PopupsTwoFactorTestViewModel);
PopupsTwoFactorTestViewModel.prototype.clearPopup = function ()
{
this.code('');
this.code.focused(false);
this.code.status(null);
this.testing(false);
};
PopupsTwoFactorTestViewModel.prototype.onShow = function ()
{
this.clearPopup();
};
PopupsTwoFactorTestViewModel.prototype.onFocus = function ()
{
this.code.focused(true);
};
/**
* @constructor
* @extends KnoinAbstractViewModel
@ -11039,6 +11053,11 @@ function LoginViewModel()
this.password = ko.observable('');
this.signMe = ko.observable(false);
this.additionalCode = ko.observable('');
this.additionalCode.error = ko.observable(false);
this.additionalCode.focused = ko.observable(false);
this.additionalCode.visibility = ko.observable(false);
this.logoImg = Utils.trim(RL.settingsGet('LoginLogo'));
this.loginDescription = Utils.trim(RL.settingsGet('LoginDescription'));
this.logoCss = Utils.trim(RL.settingsGet('LoginCss'));
@ -11053,6 +11072,8 @@ function LoginViewModel()
this.email.subscribe(function () {
this.emailError(false);
this.additionalCode('');
this.additionalCode.visibility(false);
}, this);
this.login.subscribe(function () {
@ -11063,6 +11084,14 @@ function LoginViewModel()
this.passwordError(false);
}, this);
this.additionalCode.subscribe(function () {
this.additionalCode.error(false);
}, this);
this.additionalCode.visibility.subscribe(function () {
this.additionalCode.error(false);
}, this);
this.submitRequest = ko.observable(false);
this.submitError = ko.observable('');
@ -11092,7 +11121,12 @@ function LoginViewModel()
this.emailError('' === Utils.trim(this.email()));
this.passwordError('' === Utils.trim(this.password()));
if (this.emailError() || this.passwordError())
if (this.additionalCode.visibility())
{
this.additionalCode.error('' === Utils.trim(this.additionalCode()));
}
if (this.emailError() || this.passwordError() || this.additionalCode.error())
{
return false;
}
@ -11105,12 +11139,28 @@ function LoginViewModel()
{
if (oData.Result)
{
RL.loginAndLogoutReload();
if (oData.TwoFactorAuth)
{
this.additionalCode('');
this.additionalCode.visibility(true);
this.additionalCode.focused(true);
this.submitRequest(false);
}
else
{
RL.loginAndLogoutReload();
}
}
else if (oData.ErrorCode)
{
this.submitRequest(false);
this.submitError(Utils.getNotification(oData.ErrorCode));
if ('' === this.submitError())
{
this.submitError(Utils.getNotification(Enums.Notification.UnknownError));
}
}
else
{
@ -11124,7 +11174,8 @@ function LoginViewModel()
}
}, this), this.email(), this.login(), this.password(), !!this.signMe(),
this.bSendLanguage ? this.mainLanguage() : '');
this.bSendLanguage ? this.mainLanguage() : '',
this.additionalCode.visibility() ? this.additionalCode() : '');
return true;
@ -13314,6 +13365,125 @@ SettingsIdentities.prototype.onBuild = function (oDom)
}, 50);
};
/**
* @constructor
*/
function SettingsSecurity()
{
this.processing = ko.observable(false);
this.clearing = ko.observable(false);
this.viewUser = ko.observable('');
this.viewEnable = ko.observable(false);
this.viewEnable.subs = true;
this.twoFactorStatus = ko.observable(false);
this.viewSecret = ko.observable('');
this.viewBackupCodes = ko.observable('');
this.viewUrl = ko.observable('');
this.bFirst = true;
this.viewTwoFactorStatus = ko.computed(function () {
Globals.langChangeTrigger();
return Utils.i18n(
this.twoFactorStatus() ?
'SETTINGS_SECURITY/TWO_FACTOR_SECRET_CONFIGURED_DESC' :
'SETTINGS_SECURITY/TWO_FACTOR_SECRET_NOT_CONFIGURED_DESC'
);
}, this);
this.onResult = _.bind(this.onResult, this);
}
Utils.addSettingsViewModel(SettingsSecurity, 'SettingsSecurity', 'SETTINGS_LABELS/LABEL_SECURITY_NAME', 'security');
SettingsSecurity.prototype.createTwoFactor = function ()
{
this.processing(true);
RL.remote().createTwoFactor(this.onResult);
};
SettingsSecurity.prototype.enableTwoFactor = function ()
{
this.processing(true);
RL.remote().enableTwoFactor(this.onResult, this.viewEnable());
};
SettingsSecurity.prototype.testTwoFactor = function ()
{
kn.showScreenPopup(PopupsTwoFactorTestViewModel);
};
SettingsSecurity.prototype.clearTwoFactor = function ()
{
this.viewSecret('');
this.viewBackupCodes('');
this.viewUrl('');
this.clearing(true);
RL.remote().clearTwoFactor(this.onResult);
};
SettingsSecurity.prototype.onShow = function ()
{
this.viewSecret('');
this.viewBackupCodes('');
this.viewUrl('');
};
SettingsSecurity.prototype.onResult = function (sResult, oData)
{
this.processing(false);
this.clearing(false);
if (Enums.StorageResultType.Success === sResult && oData && oData.Result)
{
this.viewUser(Utils.pString(oData.Result.User));
this.viewEnable(!!oData.Result.Enable);
this.twoFactorStatus(!!oData.Result.IsSet);
this.viewSecret(Utils.pString(oData.Result.Secret));
this.viewBackupCodes(Utils.pString(oData.Result.BackupCodes).replace(/[\s]+/g, ' '));
this.viewUrl(Utils.pString(oData.Result.Url));
}
else
{
this.viewUser('');
this.viewEnable(false);
this.twoFactorStatus(false);
this.viewSecret('');
this.viewBackupCodes('');
this.viewUrl('');
}
if (this.bFirst)
{
this.bFirst = false;
var self = this;
this.viewEnable.subscribe(function (bValue) {
if (this.viewEnable.subs)
{
RL.remote().enableTwoFactor(function (sResult, oData) {
if (Enums.StorageResultType.Success !== sResult || !oData || !oData.Result)
{
self.viewEnable.subs = false;
self.viewEnable(false);
self.viewEnable.subs = true;
}
}, bValue);
}
}, this);
}
};
SettingsSecurity.prototype.onBuild = function ()
{
this.processing(true);
RL.remote().getTwoFactor(this.onResult);
};
/**
* @constructor
*/
@ -14097,6 +14267,9 @@ function WebMailDataStorage()
this.signatureToAll = ko.observable(false);
this.replyTo = ko.observable('');
// security
this.enableTwoFactor = ko.observable(false);
// accounts
this.accounts = ko.observableArray([]);
this.accountsLoading = ko.observable(false).extend({'throttle': 100});
@ -14459,6 +14632,7 @@ WebMailDataStorage.prototype.populateDataOnStart = function()
this.replyTo(RL.settingsGet('ReplyTo'));
this.signature(RL.settingsGet('Signature'));
this.signatureToAll(!!RL.settingsGet('SignatureToAll'));
this.enableTwoFactor(!!RL.settingsGet('EnableTwoFactor'));
this.lastFoldersHash = RL.local().get(Enums.ClientSideKeyName.FoldersLashHash) || '';
@ -15542,18 +15716,74 @@ WebMailAjaxRemoteStorage.prototype.folders = function (fCallback)
* @param {string} sPassword
* @param {boolean} bSignMe
* @param {string=} sLanguage
* @param {string=} sAdditionalCode
*/
WebMailAjaxRemoteStorage.prototype.login = function (fCallback, sEmail, sLogin, sPassword, bSignMe, sLanguage)
WebMailAjaxRemoteStorage.prototype.login = function (fCallback, sEmail, sLogin, sPassword, bSignMe, sLanguage, sAdditionalCode)
{
this.defaultRequest(fCallback, 'Login', {
'Email': sEmail,
'Login': sLogin,
'Password': sPassword,
'Language': sLanguage || '',
'AdditionalCode': sAdditionalCode || '',
'SignMe': bSignMe ? '1' : '0'
});
};
/**
* @param {?Function} fCallback
*/
WebMailAjaxRemoteStorage.prototype.getTwoFactor = function (fCallback)
{
this.defaultRequest(fCallback, 'GetTwoFactorInfo');
};
/**
* @param {?Function} fCallback
*/
WebMailAjaxRemoteStorage.prototype.createTwoFactor = function (fCallback)
{
this.defaultRequest(fCallback, 'CreateTwoFactorSecret');
};
/**
* @param {?Function} fCallback
*/
WebMailAjaxRemoteStorage.prototype.clearTwoFactor = function (fCallback)
{
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
};
/**
* @param {?Function} fCallback
* @param {string} sCode
*/
WebMailAjaxRemoteStorage.prototype.testTwoFactor = function (fCallback, sCode)
{
this.defaultRequest(fCallback, 'TestTwoFactorInfo', {
'Code': sCode
});
};
/**
* @param {?Function} fCallback
* @param {boolean} bEnable
*/
WebMailAjaxRemoteStorage.prototype.enableTwoFactor = function (fCallback, bEnable)
{
this.defaultRequest(fCallback, 'EnableTwoFactor', {
'Enable': bEnable ? '1' : '0'
});
};
/**
* @param {?Function} fCallback
*/
WebMailAjaxRemoteStorage.prototype.clearTwoFactorInfo = function (fCallback)
{
this.defaultRequest(fCallback, 'ClearTwoFactorInfo');
};
/**
* @param {?Function} fCallback
* @param {string} sEmail
@ -18252,6 +18482,11 @@ RainLoopApp.prototype.bootstart = function ()
Utils.removeSettingsViewModel(SettingsOpenPGP);
}
if (!RL.settingsGet('AllowTwoFactorAuth'))
{
Utils.removeSettingsViewModel(SettingsSecurity);
}
if (!bGoogle && !bFacebook && !bTwitter)
{
Utils.removeSettingsViewModel(SettingsSocialScreen);

File diff suppressed because one or more lines are too long