summaryrefslogtreecommitdiff
path: root/frontend/delta/js/Clipperz/PM
Side-by-side diff
Diffstat (limited to 'frontend/delta/js/Clipperz/PM') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/delta/js/Clipperz/PM/BookmarkletProcessor.js191
-rw-r--r--frontend/delta/js/Clipperz/PM/Connection.js636
-rw-r--r--frontend/delta/js/Clipperz/PM/Crypto.js546
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/DirectLogin.js1086
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/DirectLoginBinding.js120
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/DirectLoginFormValue.js101
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/DirectLoginInput.js192
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/EncryptedRemoteObject.js542
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/OneTimePassword.js350
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/Record.Version.Field.js186
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js328
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/Record.js891
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.Header.Legacy.js182
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.Header.OneTimePasswords.js117
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.Header.Preferences.js48
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.Header.RecordIndex.js685
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.Subscription.js53
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/User.js827
-rw-r--r--frontend/delta/js/Clipperz/PM/Date.js196
-rw-r--r--frontend/delta/js/Clipperz/PM/PIN.js132
-rw-r--r--frontend/delta/js/Clipperz/PM/Proxy.js186
-rwxr-xr-xfrontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js86
-rw-r--r--frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js793
-rw-r--r--frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js420
-rw-r--r--frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.MemoryDataStore.js643
-rw-r--r--frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.js72
-rw-r--r--frontend/delta/js/Clipperz/PM/Proxy/Proxy.Test.js161
-rw-r--r--frontend/delta/js/Clipperz/PM/Strings.js285
-rw-r--r--frontend/delta/js/Clipperz/PM/Strings/MessagePanelConfigurations.js384
-rw-r--r--frontend/delta/js/Clipperz/PM/Strings/Strings_defaults.js385
-rw-r--r--frontend/delta/js/Clipperz/PM/Strings/Strings_en-US.js1336
-rw-r--r--frontend/delta/js/Clipperz/PM/Toll.js189
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js142
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/CardList.js161
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/ErrorPage.js46
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js150
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js122
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/PageTemplate.js33
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/RegistrationWizard.js240
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/DirectLoginController.js256
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/MainController.js491
41 files changed, 13990 insertions, 0 deletions
diff --git a/frontend/delta/js/Clipperz/PM/BookmarkletProcessor.js b/frontend/delta/js/Clipperz/PM/BookmarkletProcessor.js
new file mode 100644
index 0000000..4818b76
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/BookmarkletProcessor.js
@@ -0,0 +1,191 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+/*
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+
+Clipperz.PM.BookmarkletProcessor = function(aConfiguration) {
+ this._configuration = aConfiguration;
+
+ this._editableFields = null;
+ this._favicon = null;
+
+ return this;
+}
+
+Clipperz.PM.BookmarkletProcessor.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function() {
+ return "Clipperz.PM.BookmarkletProcessor";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'configuration': function() {
+ return this._configuration;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'pageTitle': function() {
+ return this.configuration().page.title;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'fields': function() {
+ return this.configuration().form.inputs;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'editableFields': function() {
+ if (this._editableFields == null) {
+ this._editableFields = MochiKit.Base.filter(function(aField) {
+ var result;
+ var type;
+
+ type = aField['type'].toLowerCase();
+ result = ((type != 'hidden') && (type != 'submit') && (type != 'checkbox') && (type != 'radio') && (type != 'select'));
+
+ return result;
+ }, this.fields())
+ }
+
+ return this._editableFields;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hostname': function() {
+ if (this._hostname == null) {
+ var actionUrl;
+
+ actionUrl = this.configuration()['form']['attributes']['action'];
+ this._hostname = actionUrl.replace(/ ^ h t t p s ? : \ / \ / ( [ ^ \ / ] * ) \ / . * /, '$1');
+ }
+
+ return this._hostname;
+ },
+
+ 'favicon': function() {
+ if (this._favicon == null) {
+ this._favicon = "http://" + this.hostname() + "/favicon.ico";
+ }
+
+ return this._favicon;
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
+//#############################################################################
+/ *
+Clipperz.PM.BookmarkletProcessor.createRecordFromBookmarkletConfiguration = function(anUser, aConfiguration) {
+ var processor;
+ var record;
+ var recordVersion;
+ var directLogin;
+ var bindings;
+ var i,c;
+
+ processor = new Clipperz.PM.BookmarkletProcessor(aConfiguration);
+
+ record = new Clipperz.PM.DataModel.Record({
+ 'label': processor.pageTitle(),
+ 'notes': "",
+ 'user': anUser
+ });
+ recordVersion = new Clipperz.PM.DataModel.Record.Version(record, {})
+ record.setCurrentVersion(recordVersion);
+
+ bindings = {};
+
+ c = processor.editableFields().length;
+ for (i=0; i<c; i++) {
+ var formField;
+ var recordField;
+
+ formField = processor.editableFields()[i];
+ recordField = new Clipperz.PM.DataModel.RecordField({
+ 'label': formField['name'],
+ 'value': formField['value'],
+ 'type': Clipperz.PM.Strings.inputTypeToRecordFieldType[formField['type']],
+ 'hidden': false,
+ 'recordVersion': recordVersion
+ });
+ recordVersion.addField(recordField);
+
+ bindings[formField['name']] = recordField.key();
+ }
+
+ directLogin = new Clipperz.PM.DataModel.DirectLogin({
+ 'record': record,
+ 'label': processor.pageTitle(),
+ 'favicon': processor.favicon(),
+ 'formData': processor.configuration()['form'],
+ 'bindingData': bindings,
+ 'bookmarkletVersion': '0.2'
+ });
+ record.addDirectLogin(directLogin);
+
+ anUser.addRecord(record);
+
+ return record;
+};
+* /
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.BookmarkletProcessor.sanitizeBookmarkletConfiguration = function(aConfiguration) {
+ var result;
+
+// throw "XSS Bookmarklet attempt";
+
+ result = aConfiguration;
+
+ return result;
+};
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.BookmarkletProcessor.checkBookmarkletConfiguration = function(aConfiguration) {
+ var result;
+
+ try {
+ result = Clipperz.Base.evalJSON(aConfiguration);
+ result = Clipperz.PM.BookmarkletProcessor.sanitizeBookmarkletConfiguration(result);
+
+ if (result['version'] != '0.2.3') {
+ throw "WrongBookmarkletVersion";
+ }
+ } catch (exception) {
+ throw exception;
+ }
+
+ return result;
+};
+
+//-----------------------------------------------------------------------------
+*/ \ No newline at end of file
diff --git a/frontend/delta/js/Clipperz/PM/Connection.js b/frontend/delta/js/Clipperz/PM/Connection.js
new file mode 100644
index 0000000..c02125f
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Connection.js
@@ -0,0 +1,636 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+
+//-----------------------------------------------------------------------------
+//
+// Abstract C O N N E C T I O N class
+//
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.Connection = function (args) {
+ args = args || {};
+
+ this._proxy = args.proxy || Clipperz.PM.Proxy.defaultProxy;
+ this._getCredentialsFunction = args.getCredentialsFunction;
+
+ this._clipperz_pm_crypto_version = null;
+ this._connectionId = null;
+ this._sharedSecret = null;
+ this._serverLockValue = null;
+
+ return this;
+}
+
+Clipperz.PM.Connection.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function() {
+ return "Connection [" + this.version() + "]";
+ },
+
+ //=========================================================================
+
+ 'version': function() {
+ throw Clipperz.Base.exception.AbstractMethod;
+ },
+
+ 'clipperz_pm_crypto_version': function() {
+ if (this._clipperz_pm_crypto_version == null) {
+ var connectionVersions;
+ var versions;
+ var version;
+ var i, c;
+
+ version = null;
+ connectionVersions = Clipperz.PM.Connection.communicationProtocol.versions;
+ versions = MochiKit.Base.keys(connectionVersions);
+ c = versions.length;
+ for (i=0; i<c; i++) {
+ if (! (versions[i] == 'current')) {
+ if (this instanceof connectionVersions[versions[i]]) {
+ version = versions[i];
+ };
+ }
+ }
+
+ this._clipperz_pm_crypto_version = version;
+ }
+
+ return this._clipperz_pm_crypto_version;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'defaultErrorHandler': function(anErrorString, anException) {
+// Clipperz.logError("### Connection.defaultErrorHandler: " + anErrorString, anException);
+ Clipperz.logError("### Connection.defaultErrorHandler: " + anErrorString + " (" + anException + ")");
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getCredentialsFunction': function () {
+ return this._getCredentialsFunction;
+ },
+
+ 'normalizedCredentials': function(someValues) {
+ throw Clipperz.Base.exception.AbstractMethod;
+ },
+
+ //=========================================================================
+
+ 'proxy': function () {
+ return this._proxy;
+ },
+
+ //=========================================================================
+
+ 'register': function () {
+ throw Clipperz.Base.exception.AbstractMethod;
+ },
+
+ 'login': function() {
+ throw Clipperz.Base.exception.AbstractMethod;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'message': function(someArguments, aCallback) {
+ throw Clipperz.Base.exception.AbstractMethod;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'serverSideUserCredentials': function() {
+ throw Clipperz.Base.exception.AbstractMethod;
+ },
+
+ //=========================================================================
+
+ 'sharedSecret': function () {
+ return this._sharedSecret;
+ },
+
+ 'setSharedSecret': function (aValue) {
+ this._sharedSecret = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'connectionId': function() {
+ return this._connectionId;
+ },
+
+ 'setConnectionId': function(aValue) {
+ this._connectionId = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'serverLockValue': function () {
+ return this._serverLockValue;
+ },
+
+ 'setServerLockValue': function (aValue) {
+ this._serverLockValue = aValue;
+ },
+
+ //=========================================================================
+/*
+// TODO: ?????
+ 'oneTimePassword': function() {
+ return this._oneTimePassword;
+ },
+
+ 'setOneTimePassword': function(aValue) {
+ this._oneTimePassword = aValue;
+ },
+*/
+ //=========================================================================
+
+ 'reset': function() {
+ this.setSharedSecret(null);
+ this.setConnectionId(null);
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+
+}
+);
+
+
+if (typeof(Clipperz.PM.Connection.SRP) == 'undefined') { Clipperz.PM.Connection.SRP = {}; }
+//-----------------------------------------------------------------------------
+//
+// S R P [ 1 . 0 ] C O N N E C T I O N class
+//
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.Connection.SRP['1.0'] = function (args) {
+ Clipperz.PM.Connection.call(this, args);
+
+ return this;
+}
+
+Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.PM.Connection(), {
+
+ 'version': function() {
+ return '1.0';
+ },
+
+ //=========================================================================
+
+ 'register': function (someUserData) {
+ var deferredResult;
+ var cryptoVersion;
+ var srpConnection;
+
+ cryptoVersion = this.clipperz_pm_crypto_version();
+
+ deferredResult = new Clipperz.Async.Deferred("Connection.registerWithVersion", {trace:false});
+ deferredResult.collectResults({
+ 'credentials': [
+ this.getCredentialsFunction(),
+ MochiKit.Base.method(this, 'normalizedCredentials'),
+ MochiKit.Base.bind(function(someCredentials) {
+ var srpConnection;
+ var result;
+
+ srpConnection = new Clipperz.Crypto.SRP.Connection({ C:someCredentials['username'], P:someCredentials['password'], hash:this.hash() });
+ result = srpConnection.serverSideCredentials();
+ result['version'] = Clipperz.PM.Connection.communicationProtocol.currentVersion;
+
+ return result;
+ }, this)
+ ],
+ 'user': MochiKit.Base.partial(MochiKit.Async.succeed, someUserData),
+ 'version': MochiKit.Base.partial(MochiKit.Async.succeed, Clipperz.PM.Connection.communicationProtocol.currentVersion),
+ 'message': MochiKit.Base.partial(MochiKit.Async.succeed, 'completeRegistration')
+ });
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ deferredResult.addMethod(this.proxy(), 'registration');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'updateCredentials': function (aUsername, aPassphrase, someUserData) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Connection.updateCredentials", {trace:false});
+ deferredResult.collectResults({
+ 'credentials': [
+ MochiKit.Base.method(this, 'normalizedCredentials', {username:aUsername, password:aPassphrase}),
+ MochiKit.Base.bind(function(someCredentials) {
+ var srpConnection;
+ var result;
+
+ srpConnection = new Clipperz.Crypto.SRP.Connection({ C:someCredentials['username'], P:someCredentials['password'], hash:this.hash() });
+ result = srpConnection.serverSideCredentials();
+ result['version'] = Clipperz.PM.Connection.communicationProtocol.currentVersion;
+
+ return result;
+ }, this)
+ ],
+ 'user': MochiKit.Base.partial(MochiKit.Async.succeed, someUserData)
+ });
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ deferredResult.addMethod(this, 'message', 'upgradeUserCredentials');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ deferredResult.callback();
+
+ return deferredResult;
+
+ },
+
+ //=========================================================================
+
+ 'redeemOneTimePassword': function (someParameters) {
+/*
+ //=========================================================================
+ // LOGIN WITH PASSPHRASE, extracted from the TRUNK version (LoginPanel.js)
+ deferredResult.addCallback(function(anUsername, aOneTimePassword) {
+ var args;
+
+ args = {
+ 'message': 'oneTimePassword',
+ 'version': Clipperz.PM.Crypto.communicationProtocol.currentVersion,
+ 'parameters': {
+ 'oneTimePasswordKey': Clipperz.PM.DataModel.OneTimePassword.computeKeyWithUsernameAndPassword(anUsername, aOneTimePassword),
+ 'oneTimePasswordKeyChecksum': Clipperz.PM.DataModel.OneTimePassword.computeKeyChecksumWithUsernameAndPassword(anUsername, aOneTimePassword)
+ }
+ }
+
+ return args;
+ }, anUsername, oneTimePassword);
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'updatedProgressState', 'OTP_login_loadingOTP');
+ deferredResult.addCallback(MochiKit.Base.method(Clipperz.PM.Proxy.defaultProxy, 'handshake'));
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'updatedProgressState', 'OTP_login_extractingPassphrase');
+ deferredResult.addCallback(function(aResult) {
+ return Clipperz.PM.Crypto.deferredDecrypt(oneTimePassword, aResult['data'], aResult['version']);
+ });
+ deferredResult.addCallback(function(aResult) {
+ return (new Clipperz.ByteArray().appendBase64String(aResult['passphrase'])).asString();
+ });
+ deferredResult.addMethod(this, 'doLoginWithUsernameAndPassphrase', anUsername),
+*/
+ var args;
+ var normalizedOTP;
+
+ normalizedOTP = Clipperz.PM.DataModel.OneTimePassword.normalizedOneTimePassword(someParameters['password']);
+
+ args = {
+ 'message': 'oneTimePassword',
+ 'version': Clipperz.PM.Connection.communicationProtocol.currentVersion,
+ 'parameters': {
+ 'oneTimePasswordKey': Clipperz.PM.DataModel.OneTimePassword.computeKeyWithUsernameAndPassword(someParameters['username'], normalizedOTP),
+ 'oneTimePasswordKeyChecksum': Clipperz.PM.DataModel.OneTimePassword.computeKeyChecksumWithUsernameAndPassword(someParameters['username'], normalizedOTP)
+ }
+ }
+
+ return Clipperz.Async.callbacks("Connction.redeemOTP", [
+ MochiKit.Base.method(this.proxy(), 'handshake', args),
+ function(aResult) {
+ return Clipperz.PM.Crypto.deferredDecrypt({
+ value: aResult['data'],
+ key: normalizedOTP,
+ version:aResult['version']
+ });
+ },
+ function(aResult) {
+ return (new Clipperz.ByteArray().appendBase64String(aResult['passphrase'])).asString();
+ }
+ ], {trace:false})
+ },
+
+ 'login': function(isReconnecting) {
+ var deferredResult;
+ var cryptoVersion;
+ var srpConnection;
+
+ cryptoVersion = this.clipperz_pm_crypto_version();
+ deferredResult = new Clipperz.Async.Deferred("Connection.login", {trace:false});
+ deferredResult.addCallback(this.getCredentialsFunction());
+ deferredResult.addMethod(this, 'normalizedCredentials');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'updatedProgressState', 'connection_sendingCredentials');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ deferredResult.addCallback(MochiKit.Base.bind(function(someCredentials) {
+ srpConnection = new Clipperz.Crypto.SRP.Connection({ C:someCredentials['username'], P:someCredentials['password'], hash:this.hash() });
+ }, this));
+ deferredResult.addCallback(function() {
+ var result;
+
+ result = {
+ message: 'connect',
+ version: cryptoVersion,
+ parameters: {
+ C: srpConnection.C(),
+ A: srpConnection.A().asString(16)
+// reconnecting: this.connectionId()
+ }
+ };
+
+// TODO: ?????
+// if (isReconnecting == true) {
+// args.parameters['reconnecting'] = aConnection.connectionId();
+// }
+
+ return result;
+ });
+ deferredResult.addMethod(this.proxy(), 'handshake');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'updatedProgressState', 'connection_credentialVerification');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ deferredResult.addCallback(function(someParameters) {
+ var result;
+
+ srpConnection.set_s(new Clipperz.Crypto.BigInt(someParameters['s'], 16));
+ srpConnection.set_B(new Clipperz.Crypto.BigInt(someParameters['B'], 16));
+
+// TODO: ?????
+// if (typeof(someParameters['oneTimePassword']) != 'undefined') {
+// this.setOneTimePassword(someParameters['oneTimePassword']);
+// }
+
+ result = {
+ message: 'credentialCheck',
+ version: cryptoVersion,
+ parameters: {
+ M1: srpConnection.M1()
+ }
+ };
+
+ return result;
+ });
+ deferredResult.addMethod(this.proxy(), 'handshake');
+ deferredResult.addCallback(function(someParameters) {
+ var result;
+
+ if (someParameters['M2'] == srpConnection.M2()) {
+ result = MochiKit.Async.succeed(someParameters);
+ } else {
+ result = MochiKit.Async.fail(Clipperz.PM.Connection.exception.WrongChecksum);
+ }
+
+ return result;
+ });
+ deferredResult.addCallback(MochiKit.Base.bind(function(someParameters) {
+ this.setConnectionId(someParameters['connectionId']);
+ this.setSharedSecret(srpConnection.K());
+// TODO: ?????
+// if (this.oneTimePassword() != null) {
+/// ?? result = this.user().oneTimePasswordManager().archiveOneTimePassword(this.oneTimePassword()));
+// }
+
+ if ((isReconnecting == true) && (this.serverLockValue() != someParameters['lock'])) {
+ throw Clipperz.PM.Connection.exception.StaleData;
+ } else {
+ this.setServerLockValue(someParameters['lock']);
+ }
+
+ return someParameters;
+ }, this));
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'updatedProgressState', 'connection_loggedIn');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+// deferredResult.addCallback(MochiKit.Async.succeed, {result:"done"});
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'logout': function() {
+ return Clipperz.Async.callbacks("Connection.logout", [
+ MochiKit.Base.method(this, 'setSharedSecret'),
+ MochiKit.Base.method(this.proxy(), 'logout', {})
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'ping': function () {
+ // TODO: ping the server in order to have a valid session
+ },
+
+ //=========================================================================
+
+ 'message': function(aMessageName, someParameters) {
+ var args;
+ var parameters;
+
+ parameters = someParameters || {};
+ if (typeof(parameters['user']) != 'undefined') {
+ parameters['user']['lock'] = this.serverLockValue();
+ }
+
+ args = {
+ message: aMessageName,
+ srpSharedSecret: this.sharedSecret(),
+// parameters: (someParameters || {})
+ parameters: parameters
+ }
+
+ return this.sendMessage(args);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'sendMessage': function(someArguments) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Connection.sendMessage", {trace:false});
+ deferredResult.addMethod(this.proxy(), 'message', someArguments);
+ deferredResult.addCallback(MochiKit.Base.bind(function(res) {
+ if (typeof(res['lock']) != 'undefined') {
+ this.setServerLockValue(res['lock']);
+ }
+ return res;
+ }, this));
+
+ deferredResult.addErrback(MochiKit.Base.method(this, 'messageExceptionHandler'), someArguments);
+ deferredResult.callback();
+
+ return deferredResult
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'messageExceptionHandler': function(anOriginalMessageArguments, anError) {
+ var result;
+
+Clipperz.log(">>> Connection.messageExceptionHandler: " + anError.message, anError);
+ if (anError instanceof MochiKit.Async.CancelledError) {
+ result = anError;
+ } else {
+ if ((anError.message == 'Trying to communicate without an active connection') ||
+ (anError.message == 'No tollManager available for current session')
+ ) {
+ result = this.reestablishConnection(anOriginalMessageArguments);
+ } else if (anError.message == 'Session with stale data') {
+ MochiKit.Signal.signal(this, 'EXCEPTION');
+ } else {
+ result = anError;
+ }
+ }
+Clipperz.log("<<< Connection.messageExceptionHandler")
+
+ return result;;
+ },
+
+ //=========================================================================
+
+ 'reestablishConnection': function(anOriginalMessageArguments) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Connection.reestablishConnection");
+ deferredResult.addMethod(this, 'reset');
+ deferredResult.addMethod(this, 'login', true);
+ deferredResult.addCallback(MochiKit.Base.bind(function(aMessage) {
+ aMessage['srpSharedSecret'] = this.sharedSecret();
+ return aMessage;
+ }, this), anOriginalMessageArguments);
+ deferredResult.addMethod(this, 'sendMessage');
+ deferredResult.addErrback(MochiKit.Signal.signal, this, 'EXCEPTION', null);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'serverSideUserCredentials': function(aUsername, aPassword) {
+ var result;
+ var newSrpConnection;
+ var normalizedAttributes;
+
+ normalizedAttributes = this.normalizedCredentials({username:aUsername, password:aPassword});
+ newSrpConnection = new Clipperz.Crypto.SRP.Connection({ C:normalizedAttributes['username'], P:normalizedAttributes['password'], hash:this.hash() });
+ result = newSrpConnection.serverSideCredentials();
+ result['version'] = this.clipperz_pm_crypto_version();
+
+ return result;
+ },
+
+ //=========================================================================
+
+ 'normalizedCredentials': function(someValues) {
+ var result;
+
+ result = {}
+ result['username'] = this.hash()(new Clipperz.ByteArray(someValues['username'])).toHexString().substring(2);
+ result['password'] = this.hash()(new Clipperz.ByteArray(someValues['password'] + someValues['username'])).toHexString().substring(2);
+
+ return result;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'hash': function() {
+ return Clipperz.PM.Crypto.encryptingFunctions.versions['0.1'].hash;
+ },
+
+ //-----------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+
+});
+
+
+
+//-----------------------------------------------------------------------------
+//
+// S R P [ 1 . 1 ] C O N N E C T I O N class
+//
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.Connection.SRP['1.1'] = function (args) {
+ Clipperz.PM.Connection.SRP['1.0'].call(this, args);
+
+ return this;
+}
+
+Clipperz.PM.Connection.SRP['1.1'].prototype = MochiKit.Base.update(new Clipperz.PM.Connection.SRP['1.0'](), {
+
+ 'version': function() {
+ return '1.1';
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'normalizedCredentials': function(someValues) {
+ var result;
+
+ result = {}
+ result['username'] = this.hash()(new Clipperz.ByteArray(someValues['username'] + someValues['password'])).toHexString().substring(2);
+ result['password'] = this.hash()(new Clipperz.ByteArray(someValues['password'] + someValues['username'])).toHexString().substring(2);
+
+ return result;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'hash': function() {
+ return Clipperz.PM.Crypto.encryptingFunctions.versions['0.2'].hash;
+ },
+
+ //-----------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+
+});
+
+Clipperz.PM.Connection.exception = {
+ WrongChecksum: new MochiKit.Base.NamedError("Clipperz.ByteArray.exception.InvalidValue"),
+ StaleData: new MochiKit.Base.NamedError("Stale data"),
+ UnexpectedRequest: new MochiKit.Base.NamedError("Clipperz.ByteArray.exception.UnexpectedRequest")
+};
+
+
+Clipperz.PM.Connection.communicationProtocol = {
+ 'currentVersion': '0.2',
+ 'versions': {
+ '0.1': Clipperz.PM.Connection.SRP['1.0'], //Clipperz.Crypto.SRP.versions['1.0'].Connection,
+ '0.2': Clipperz.PM.Connection.SRP['1.1'] //Clipperz.Crypto.SRP.versions['1.1'].Connection
+ },
+ 'fallbackVersions': {
+// 'current': '0.1',
+ '0.2': '0.1',
+ '0.1': null
+ }
+};
+
+MochiKit.Base.update(Clipperz.PM.Connection.communicationProtocol.versions, {
+ 'current': Clipperz.PM.Connection.communicationProtocol.versions[Clipperz.PM.Connection.communicationProtocol.currentVersion]
+});
+
+MochiKit.Base.update(Clipperz.PM.Connection.communicationProtocol.fallbackVersions, {
+ 'current': Clipperz.PM.Connection.communicationProtocol.fallbackVersions[Clipperz.PM.Connection.communicationProtocol.currentVersion]
+});
+
+
+
diff --git a/frontend/delta/js/Clipperz/PM/Crypto.js b/frontend/delta/js/Clipperz/PM/Crypto.js
new file mode 100644
index 0000000..7edf17f
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Crypto.js
@@ -0,0 +1,546 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.Crypto) == 'undefined') { Clipperz.PM.Crypto = {}; }
+
+Clipperz.PM.Crypto.VERSION = "0.2";
+Clipperz.PM.Crypto.NAME = "Clipperz.PM.Crypto";
+
+Clipperz.PM.Crypto.encryptingFunctions = {};
+
+MochiKit.Base.update(Clipperz.PM.Crypto, {
+
+ '__repr__': function () {
+ return "[" + this.NAME + " " + this.VERSION + "]";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'toString': function () {
+ return this.__repr__();
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'communicationProtocol': {
+ 'currentVersion': '0.2',
+ 'versions': {
+ '0.1': Clipperz.PM.Connection.SRP['1.0'], //Clipperz.Crypto.SRP.versions['1.0'].Connection,
+ '0.2': Clipperz.PM.Connection.SRP['1.1'] //Clipperz.Crypto.SRP.versions['1.1'].Connection
+ },
+ 'fallbackVersions': {
+ 'current': '0.1',
+ '0.2': '0.1',
+ '0.1': null
+ }
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'encryptingFunctions': {
+ 'currentVersion': '0.4',
+ 'versions': {
+
+ //#####################################################################
+
+ '0.1': {
+ 'encrypt': function(aKey, aValue) {
+ return Clipperz.Crypto.Base.encryptUsingSecretKey(aKey, Clipperz.Base.serializeJSON(aValue));
+ },
+
+ 'deferredEncrypt': function(aKey, aValue) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Crypto[0.1].deferredEncrypt");
+ deferredResult.addCallback(Clipperz.PM.Crypto.encryptingFunctions.versions['0.1'].encrypt, aKey, aValue);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ 'decrypt': function(aKey, aValue) {
+ var result;
+
+ if (aValue != null) {
+ result = Clipperz.Base.evalJSON(Clipperz.Crypto.Base.decryptUsingSecretKey(aKey, aValue));
+ } else {
+ result = null;
+ }
+
+ return result;
+ },
+
+ 'deferredDecrypt': function(aKey, aValue) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Crypto.[0.1].deferredDecrypt");
+ deferredResult.addCallback(Clipperz.PM.Crypto.encryptingFunctions.versions['0.1'].decrypt, aKey, aValue);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ 'hash': function(aValue) {
+ var result;
+ var strngResult;
+
+ stringResult = Clipperz.Crypto.Base.computeHashValue(aValue.asString()); // !!!!!!!
+ result = new Clipperz.ByteArray("0x" + stringResult);
+
+ return result;
+ },
+
+ 'deriveKey': function(aStringValue) {
+ return Clipperz.Crypto.Base.computeHashValue(aStringValue);
+ }
+ },
+
+ //#####################################################################
+
+ '0.2': {
+ 'encrypt': function(aKey, aValue, aNonce) {
+ var result;
+ var key, value;
+ var dataToEncrypt;
+ var encryptedData;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = new Clipperz.ByteArray(Clipperz.Base.serializeJSON(aValue));
+ dataToEncrypt = Clipperz.Crypto.SHA.sha_d256(value).appendBlock(value);
+ encryptedData = Clipperz.Crypto.AES.encrypt(key, dataToEncrypt, aNonce);
+ result = encryptedData.toBase64String();
+
+ return result;
+ },
+
+ 'deferredEncrypt': function(aKey, aValue, aNonce) {
+ var deferredResult;
+ var key, value;
+ var dataToEncrypt;
+// var encryptedData;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = new Clipperz.ByteArray(Clipperz.Base.serializeJSON(aValue));
+ dataToEncrypt = Clipperz.Crypto.SHA.sha_d256(value).appendBlock(value);
+
+ deferredResult = new Clipperz.Async.Deferred("Crypto[0.2].deferredEncrypt")
+ deferredResult.addCallback(Clipperz.Crypto.AES.deferredEncrypt, key, dataToEncrypt, aNonce);
+ deferredResult.addCallback(function(aResult) {
+ return aResult.toBase64String();
+ })
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ 'decrypt': function(aKey, aValue) {
+ var result;
+
+ if (aValue != null) {
+ var key, value;
+ var decryptedData;
+ var decryptedValue;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = new Clipperz.ByteArray().appendBase64String(aValue);
+
+ decryptedData = Clipperz.Crypto.AES.decrypt(key, value);
+ decryptedValue = decryptedData.split((256/8));
+
+ try {
+ result = Clipperz.Base.evalJSON(decryptedValue.asString());
+ } catch (exception) {
+ Clipperz.logError("Error while decrypting data [1]");
+ throw Clipperz.Crypto.Base.exception.CorruptedMessage;
+ }
+ } else {
+ result = null;
+ }
+
+ return result;
+ },
+
+ 'deferredDecrypt': function(aKey, aValue) {
+ var result;
+
+ if (aValue != null) {
+ var deferredResult;
+ var key, value;
+// var decryptedData;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = new Clipperz.ByteArray().appendBase64String(aValue);
+
+ deferredResult = new Clipperz.Async.Deferred("Crypto.[0.2].deferredDecrypt");
+ deferredResult.addCallback(Clipperz.Crypto.AES.deferredDecrypt, key, value);
+ deferredResult.addCallback(function(aResult) {
+ var result;
+ var decryptedData;
+
+ decryptedData = aResult.split((256/8));
+
+ try {
+ result = Clipperz.Base.evalJSON(decryptedData.asString());
+ } catch (exception) {
+ Clipperz.logError("Error while decrypting data [2]");
+ throw Clipperz.Crypto.Base.exception.CorruptedMessage;
+ }
+
+ return result;
+ })
+ deferredResult.callback();
+
+ result = deferredResult;
+ } else {
+ result = MochiKit.Async.succeed(null);
+ }
+
+ return result;
+ },
+
+ 'hash': Clipperz.Crypto.SHA.sha_d256,
+
+ 'deriveKey': function(aStringValue) {
+ var byteData;
+ var result;
+
+ byteData = new Clipperz.ByteArray(aStringValue);
+ result = Clipperz.Crypto.SHA.sha_d256(byteData);
+
+ return result;
+ }
+ },
+
+ //#####################################################################
+
+ '0.3': {
+ 'encrypt': function(aKey, aValue, aNonce) {
+ var result;
+ var key, value;
+ var data;
+ var dataToEncrypt;
+ var encryptedData;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = Clipperz.Base.serializeJSON(aValue);
+ data = new Clipperz.ByteArray(value);
+ encryptedData = Clipperz.Crypto.AES.encrypt(key, data, aNonce);
+ result = encryptedData.toBase64String();
+
+ return result;
+ },
+
+ 'deferredEncrypt': function(aKey, aValue, aNonce) {
+ var deferredResult;
+ var key, value;
+ var data;
+ var dataToEncrypt;
+ var encryptedData;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = Clipperz.Base.serializeJSON(aValue);
+ data = new Clipperz.ByteArray(value);
+
+ deferredResult = new Clipperz.Async.Deferred("Crypto[0.3].deferredEncrypt")
+ deferredResult.addCallback(Clipperz.Crypto.AES.deferredEncrypt, key, data, aNonce);
+ deferredResult.addCallback(function(aResult) {
+ return aResult.toBase64String();
+ })
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ 'decrypt': function(aKey, aValue) {
+ var result;
+
+ if (aValue != null) {
+ var key, value;
+ var decryptedData;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = new Clipperz.ByteArray().appendBase64String(aValue);
+
+ decryptedData = Clipperz.Crypto.AES.decrypt(key, value);
+
+ value = decryptedData.asString();
+ try {
+ result = Clipperz.Base.evalJSON(value);
+ } catch (exception) {
+ Clipperz.logError("Error while decrypting data [3]");
+ throw Clipperz.Crypto.Base.exception.CorruptedMessage;
+ }
+ } else {
+ result = null;
+ }
+
+ return result;
+ },
+
+ 'deferredDecrypt': function(aKey, aValue) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Crypto[0.3].deferredDecrypt", {trace: false});
+// now = new Date;
+
+ if (aValue != null) {
+ var key, value;
+// var decryptedData;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = new Clipperz.ByteArray().appendBase64String(aValue);
+
+ deferredResult.addCallback(Clipperz.Crypto.AES.deferredDecrypt, key, value);
+ deferredResult.addCallback(MochiKit.Async.wait, 0.1);
+ deferredResult.addCallback(function(aResult) {
+ return aResult.asString();
+ });
+ deferredResult.addCallback(MochiKit.Async.wait, 0.1);
+ deferredResult.addCallback(Clipperz.Base.evalJSON);
+ deferredResult.addErrback(function(anError) {
+console.log("PIPPO_1", anError)
+ Clipperz.logError("Error while decrypting data [4]");
+ throw Clipperz.Crypto.Base.exception.CorruptedMessage;
+ })
+ } else {
+ deferredResult.addCallback(function() {
+ return null;
+ });
+ }
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ 'hash': Clipperz.Crypto.SHA.sha_d256,
+
+ 'deriveKey': function(aStringValue) {
+ var byteData;
+ var result;
+
+ byteData = new Clipperz.ByteArray(aStringValue);
+ result = Clipperz.Crypto.SHA.sha_d256(byteData);
+
+ return result;
+ }
+ },
+
+ //#####################################################################
+
+ '0.4': {
+ 'encrypt': function(aKey, aValue, aNonce) {
+ var result;
+ var key, value;
+ var data;
+ var dataToEncrypt;
+ var encryptedData;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = Clipperz.Base.serializeJSON(aValue);
+ data = new Clipperz.ByteArray(value);
+ encryptedData = Clipperz.Crypto.AES_2.encrypt(key, data, aNonce);
+ result = encryptedData.toBase64String();
+
+ return result;
+ },
+
+ 'deferredEncrypt': function(aKey, aValue, aNonce) {
+ var deferredResult;
+ var key, value;
+ var data;
+ var dataToEncrypt;
+ var encryptedData;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = Clipperz.Base.serializeJSON(aValue);
+ data = new Clipperz.ByteArray(value);
+
+ deferredResult = new Clipperz.Async.Deferred("Crypto[0.4].deferredEncrypt")
+ deferredResult.addCallback(Clipperz.Crypto.AES_2.deferredEncrypt, key, data, aNonce);
+ deferredResult.addCallback(function(aResult) {
+ return aResult.toBase64String();
+ })
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ 'decrypt': function(aKey, aValue) {
+ var result;
+
+ if (aValue != null) {
+ var key, value;
+ var decryptedData;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = new Clipperz.ByteArray().appendBase64String(aValue);
+
+ decryptedData = Clipperz.Crypto.AES_2.decrypt(key, value);
+
+ value = decryptedData.asString();
+ try {
+ result = Clipperz.Base.evalJSON(value);
+ } catch (exception) {
+ console.log("PIPPO_2", anError)
+ Clipperz.logError("Error while decrypting data [4]");
+ throw Clipperz.Crypto.Base.exception.CorruptedMessage;
+ }
+ } else {
+ result = null;
+ }
+
+ return result;
+ },
+
+ 'deferredDecrypt': function(aKey, aValue) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Crypto[0.4].deferredDecrypt", {trace: false});
+
+ if (aValue != null) {
+ var key, value;
+
+ key = Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aKey));
+ value = new Clipperz.ByteArray().appendBase64String(aValue);
+
+ deferredResult.addCallback(Clipperz.Crypto.AES_2.deferredDecrypt, key, value);
+ deferredResult.addCallback(MochiKit.Async.wait, 0.1);
+ deferredResult.addCallback(function(aResult) {
+ return aResult.asString();
+ });
+ deferredResult.addCallback(MochiKit.Async.wait, 0.1);
+ deferredResult.addCallback(Clipperz.Base.evalJSON);
+ deferredResult.addErrback(function(anError) {
+ Clipperz.logError("Error while decrypting data [4]");
+ throw Clipperz.Crypto.Base.exception.CorruptedMessage;
+ })
+ } else {
+ deferredResult.addCallback(function() {
+ return null;
+ });
+ }
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ 'hash': Clipperz.Crypto.SHA.sha_d256,
+
+ 'deriveKey': function(aStringValue) {
+ var byteData;
+ var result;
+
+ byteData = new Clipperz.ByteArray(aStringValue);
+ result = Clipperz.Crypto.SHA.sha_d256(byteData);
+
+ return result;
+ }
+ },
+
+ //#####################################################################
+ __syntaxFix__: "syntax fix"
+ }
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'encrypt': function(aKey, aValue, aVersion) {
+ return Clipperz.PM.Crypto.encryptingFunctions.versions[aVersion].encrypt(aKey, aValue);
+ },
+
+ 'deferredEncrypt': function(someParameters) {
+ return Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters['version']].deferredEncrypt(someParameters['key'], someParameters['value']);
+ },
+
+ //.........................................................................
+
+ 'decrypt': function(aKey, aValue, aVersion) {
+ return Clipperz.PM.Crypto.encryptingFunctions.versions[aVersion].decrypt(aKey, aValue);
+ },
+
+ 'deferredDecrypt': function(someParameters) {
+ return Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters['version']].deferredDecrypt(someParameters['key'], someParameters['value']);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hash': function(aValue) {
+ return Clipperz.PM.Crypto.encryptingFunctions.versions[Clipperz.PM.Crypto.encryptingFunctions.currentVersion]['hash'](aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'randomKey': function() {
+ return Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(32).toHexString().substring(2);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'deriveKey': function(aValue) {
+ return Clipperz.PM.Crypto.encryptingFunctions.versions[Clipperz.PM.Crypto.encryptingFunctions.currentVersion].deriveKey(aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'passwordEntropy': function(aValue) {
+ var result;
+ var bitPerChar;
+
+ bitPerChar = 4;
+ if (/[a-z]/.test(aValue)) {
+ bitPerChar ++;
+ }
+ if (/[A-Z]/.test(aValue)) {
+ bitPerChar ++;
+ }
+ if (/[^a-zA-Z0-9]/.test(aValue)) {
+ bitPerChar ++;
+ }
+
+ result = aValue.length * bitPerChar;
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'nullValue': '####',
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+
+});
+
+//*****************************************************************************
+
+//MochiKit.Base.update(Clipperz.PM.Connection.communicationProtocol.versions, {
+// 'current': Clipperz.PM.Connection.communicationProtocol.versions[Clipperz.PM.Connection.communicationProtocol.currentVersion]
+//});
+
+MochiKit.Base.update(Clipperz.PM.Crypto.encryptingFunctions.versions, {
+ 'current': Clipperz.PM.Crypto.encryptingFunctions.versions[Clipperz.PM.Crypto.encryptingFunctions.currentVersion]
+});
+
+//*****************************************************************************
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/DirectLogin.js b/frontend/delta/js/Clipperz/PM/DataModel/DirectLogin.js
new file mode 100644
index 0000000..8db90de
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/DirectLogin.js
@@ -0,0 +1,1086 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.Base.module('Clipperz.PM.DataModel');
+
+Clipperz.PM.DataModel.DirectLogin = function(args) {
+ args = args || {};
+
+ Clipperz.PM.DataModel.DirectLogin.superclass.constructor.apply(this, arguments);
+
+ this._reference = args.reference
+ || Clipperz.PM.Crypto.randomKey();
+ this._record = args.record
+ || Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._retrieveIndexDataFunction = args.retrieveIndexDataFunction
+ || this.record().retrieveDirectLoginIndexDataFunction()
+ || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._setIndexDataFunction = args.setIndexDataFunction
+ || this.record().setDirectLoginIndexDataFunction()
+ || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._removeIndexDataFunction = args.removeIndexDataFunction
+ || this.record().removeDirectLoginIndexDataFunction()
+ || Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._inputs = null;
+ this._bindings = null;
+ this._formValues = null;
+
+// this._inputsDeferredLock = new MochiKit.Async.DeferredLock();
+// this._bindingsDeferredLock = new MochiKit.Async.DeferredLock();
+// this._formValuesDeferredLock = new MochiKit.Async.DeferredLock();
+
+ this._transientState = null;
+
+ this._isBrandNew = MochiKit.Base.isUndefinedOrNull(args.reference);
+
+ this.record().addDirectLogin(this);
+
+ return this;
+}
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.DirectLogin, Object, {
+
+ 'toString': function() {
+ return "DirectLogin (" + this.reference() + ")";
+ },
+
+ //=========================================================================
+
+ 'reference': function () {
+ return this._reference;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'record': function () {
+ return this._record;
+ },
+
+ //=========================================================================
+
+ 'isBrandNew': function () {
+ return this._isBrandNew;
+ },
+
+ //=========================================================================
+
+ 'removeIndexDataFunction': function () {
+ return this._removeIndexDataFunction;
+ },
+
+ 'remove': function () {
+ return Clipperz.Async.callbacks("DirectLogin.remove", [
+ MochiKit.Base.partial(this.removeIndexDataFunction(), this.reference()),
+ MochiKit.Base.method(this.record(), 'removeDirectLogin', this)
+ ], {trace:false});
+ },
+
+ //=========================================================================
+/*
+ 'inputsDeferredLock': function () {
+ return this._inputsDeferredLock;
+ },
+
+ 'bindingsDeferredLock': function () {
+ return this._bindingsDeferredLock;
+ },
+
+ 'formValuesDeferredLock': function () {
+ return this._formValuesDeferredLock;
+ },
+*/
+ //=========================================================================
+
+ 'label': function () {
+ return this.getIndexDataForKey('label');
+ },
+
+ 'setLabelKeepingBackwardCompatibilityWithBeta': function (aValue) {
+ return Clipperz.Async.callbacks("DirectLogin.setLabelKeepingBackwardCompatibilityWithBeta", [
+ MochiKit.Base.method(this, 'setIndexDataForKey', 'label', aValue),
+ MochiKit.Base.method(this, 'setValue', 'label', aValue)
+ ], {trace:false});
+ },
+
+ 'setLabel': function (aValue) {
+ return this.setLabelKeepingBackwardCompatibilityWithBeta(aValue);
+// return this.setIndexDataForKey('label', aValue);
+ },
+
+ //=========================================================================
+
+ 'favicon': function () {
+ return this.getIndexDataForKey('favicon');
+ },
+
+ 'setFavicon': function (aValue) {
+ return this.setIndexDataForKey('favicon', aValue);
+ },
+
+ 'faviconUrlWithBookmarkletConfiguration': function (aBookmarkletConfiguration) {
+ var result;
+
+ if (! MochiKit.Base.isUndefinedOrNull(aBookmarkletConfiguration['page']['favicon'])) {
+ result = aBookmarkletConfiguration['page']['favicon'];
+ } else if (! MochiKit.Base.isUndefinedOrNull(aBookmarkletConfiguration['form']['attributes']['action'])) {
+ var actionUrl;
+ var hostname;
+
+ actionUrl = aBookmarkletConfiguration['form']['attributes']['action'];
+ hostname = actionUrl.replace(/^https?:\/\/([^\/]*)\/.*/, '$1');
+ result = "http://" + hostname + "/favicon.ico";
+ } else {
+ result = null;
+ }
+
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'faviconData': function () {
+ var regexp = new RegExp('^data\:\/\/.*', 'i');
+
+ return Clipperz.Async.callbacks("DirectLogin.favicon", [
+ MochiKit.Base.method(this, 'getIndexDataForKey', 'favicon'),
+ MochiKit.Base.method(regexp, 'test'),
+ Clipperz.Async.deferredIf("is data URL", [
+ MochiKit.Base.method(this, 'getIndexDataForKey', 'favicon')
+ ], [
+ MochiKit.Base.method(this, 'transientState'),
+ MochiKit.Base.itemgetter('faviconData'),
+ Clipperz.Async.deferredIf('has a chaced value for the favicon data', [
+ MochiKit.Base.operator.identity
+ ], [
+ MochiKit.Base.method(this, 'getIndexDataForKey', 'favicon'),
+ MochiKit.Base.method(this, 'loadFaviconDataFromURL')
+ ])
+
+ ])
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'loadFaviconDataFromURL': function (anURL) {
+ var deferredResult;
+ var image;
+
+ deferredResult = new Clipperz.Async.Deferred("DirectLogin.loadFaviconDataFromURL", {trace:false});
+ deferredResult.addCallback(function (anEvent) {
+ var image = anEvent.src();
+ var canvas = document.createElement("canvas");
+ var result;
+
+ canvas.width = image.width;
+ canvas.height = image.height;
+
+ var ctx = canvas.getContext("2d");
+ ctx.drawImage(image, 0, 0);
+
+ result = canvas.toDataURL(/*"image/png"* /);
+
+ return result;
+ });
+ deferredResult.addErrback(MochiKit.Async.succeed, Clipperz.PM.Strings.getValue('defaultFaviconUrl'));
+ deferredResult.addBoth(MochiKit.Base.bind(function (aDataUrl) {
+ this.transientState()['faviconData'] = aDataUrl;
+
+ return aDataUrl;
+ }, this));
+
+ image = new Image();
+ MochiKit.Signal.connect(image, 'onload', MochiKit.Base.method(deferredResult, 'callback'));
+ MochiKit.Signal.connect(image, 'onerror', MochiKit.Base.method(deferredResult, 'errback'));
+ MochiKit.Signal.connect(image, 'onabort', MochiKit.Base.method(deferredResult, 'errback'));
+
+ image.src = anURL;
+
+ return deferredResult;
+ },
+*/
+
+ //=========================================================================
+
+ 'type': function () {
+ return this.getValue('formData.attributes.type')
+ },
+
+ //=========================================================================
+
+ 'serializedData': function () {
+ return Clipperz.Async.collectResults("DirectLogin.serializedData", {
+ 'bookmarkletVersion': MochiKit.Base.method(this, 'getValue', 'bookmarkletVersion'),
+ 'formData': MochiKit.Base.method(this, 'getValue', 'formData'),
+ 'formValues': MochiKit.Base.method(this, 'getValue', 'formValues'),
+ 'bindingData': [
+ MochiKit.Base.method(this, 'bindings'),
+ function (someBindings) {
+ var result;
+ var bindingKey;
+
+ result = {}
+ for (bindingKey in someBindings) {
+ result[bindingKey] = someBindings[bindingKey].serializedData();
+ }
+
+ return result;
+ }
+ ]
+ }, {trace:false})()
+ },
+
+ //=========================================================================
+/*
+ 'fixFormDataFromBookmarkletVersion_0_1': function(aValue) {
+//{"type":"radio", "name":"action", "value":"new-user", "checked":false }, { "type":"radio", "name":"action", "value":"sign-in", "checked":true }
+// ||
+// \ /
+// \/
+//{"name":"dominio", "type":"radio", "options":[{"value":"@alice.it", "checked":true}, {"value":"@tin.it", "checked":false}, {"value":"@virgilio.it", "checked":false}, {"value":"@tim.it", "checked":false}]}
+ var result;
+ var inputs;
+ var updatedInputs;
+ var radios;
+
+ result = aValue;
+ inputs = aValue['inputs'];
+
+ updatedInputs = MochiKit.Base.filter(function(anInput) {
+ var result;
+ var type;
+
+ type = anInput['type'] || 'text';
+ result = type.toLowerCase() != 'radio';
+
+ return result;
+ }, inputs);
+ radios = MochiKit.Base.filter(function(anInput) {
+ var result;
+ var type;
+
+ type = anInput['type'] || 'text';
+ result = type.toLowerCase() == 'radio';
+
+ return result;
+ }, inputs);
+
+ if (radios.length > 0) {
+ var updatedRadios;
+
+ updatedRadios = {};
+ MochiKit.Iter.forEach(radios, MochiKit.Base.bind(function(aRadio) {
+ var radioConfiguration;
+
+ radioConfiguration = updatedRadios[aRadio['name']];
+ if (radioConfiguration == null) {
+ radioConfiguration = {type:'radio', name:aRadio['name'], options:[]};
+ updatedRadios[aRadio['name']] = radioConfiguration;
+ }
+
+// TODO: remove the value: field and replace it with element.dom.value = <some value>
+ radioConfiguration.options.push({value:aRadio['value'], checked:aRadio['checked']});
+
+// TODO: shoud remove the 'formValues' call, as it is now deferred
+// if ((aRadio['checked'] == true) && (this.formValues()[aRadio['name']] == null)) {
+// this.formValues()[aRadio['name']] = aRadio['value'];
+// }
+ }, this))
+
+ updatedInputs = MochiKit.Base.concat(updatedInputs, MochiKit.Base.values(updatedRadios));
+ }
+
+ delete result.inputs;
+ result.inputs = updatedInputs;
+
+ return result;
+ },
+
+ '_fixConfiguration': function (aConfiguration) {
+ var fixedConfiguration;
+// var inputs;
+// var bindings;
+// var i,c;
+
+ fixedConfiguration = Clipperz.Base.deepClone(aConfiguration);
+
+//Clipperz.log("PROCESS CONFIGURATION", aConfiguration);
+ switch (aConfiguration['bookmarkletVersion']) {
+ case '0.1':
+ fixedConfiguration['formData'] = this.fixFormDataFromBookmarkletVersion_0_1(aConfiguration['formData']);
+ break;
+ case '0.2':
+ fixedConfiguration['formData'] = aConfiguration['formData'];
+ break;
+ }
+
+/ *
+ aConfiguration['_inputs'] = [];
+ c = formData['inputs'].length;
+ for (i=0; i<c; i++) {
+ aConfiguration['_inputs'].push(new Clipperz.PM.DataModel.DirectLoginInput(formData['inputs'][i]));
+ }
+* /
+/ *
+ aConfiguration['_bindings'] = {};
+ if (aConfiguration['legacyBindingData'] == null) {
+ if (aConfiguration['bindingData'] != null) {
+ var bindingKey;
+
+ for (bindingKey in aConfiguration['bindingData']) {
+ var newBinding;
+
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(bindingKey, {fieldKey:aConfiguration['bindingData'][bindingKey]});
+ aConfiguration['_bindings'][newBinding.key()] = newBinding;
+ }
+ } else {
+ var editableFields;
+
+ editableFields = MochiKit.Base.filter(function(aField) {
+ var result;
+ var type;
+
+ type = aField['type'].toLowerCase();
+ result = ((type != 'hidden') && (type != 'submit') && (type != 'checkbox') && (type != 'radio') && (type != 'select'));
+
+ return result;
+ }, aConfiguration['_inputs']);
+
+ MochiKit.Iter.forEach(editableFields, MochiKit.Base.bind(function(anEditableField) {
+ var newBinding;
+
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(anEditableField['name']);
+ aConfiguration['_bindings'][newBinding.key()] = newBinding;
+ }, this));
+ }
+
+ } else {
+ var bindingKey;
+
+ for (bindingKey in aConfiguration['legacyBindingData']) {
+ var newBinding;
+
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(bindingKey, {fieldName:aConfiguration['legacyBindingData'][bindingKey]});
+ aConfiguration['_bindings'][newBinding.key()] = newBinding;
+ }
+ }
+* /
+
+ return fixedConfiguration;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getObjectDataStore': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("DirectLogin.getObjectDataStore", {trace:false});
+ deferredResult.acquireLock(this.objectDataStoreDeferredLock());
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._objectDataStore == null) {
+ this._objectDataStore = new Clipperz.KeyValueObjectStore();
+
+ innerDeferredResult = new Clipperz.Async.Deferred("DirectLogin.getObjectDataStore <inner deferred>", {trace:false});
+// innerDeferredResult.addMethod(this.record(), 'getValue', 'directLogins' + '.' + this.reference());
+ innerDeferredResult.addMethod(this, 'getValue', ''),
+ innerDeferredResult.addMethod(this, 'setOriginalState');
+ innerDeferredResult.addMethod(this, '_fixConfiguration');
+ innerDeferredResult.addMethod(this._objectDataStore, 'initWithValues');
+// innerDeferredResult.addMethod(this._objectDataStore, 'setValues');
+ innerDeferredResult.callback();
+ } else {
+ innerDeferredResult = MochiKit.Async.succeed(this._objectDataStore);
+ }
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(this.objectDataStoreDeferredLock());
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasInitiatedObjectDataStore': function () {
+ return (this._objectDataStore != null);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'resetObjectDataStore': function () {
+ this._objectDataStore.removeAllData();
+ this._objectDataStore = null;
+ },
+*/
+ //=========================================================================
+
+ 'bookmarkletConfiguration': function () {
+ return Clipperz.Async.callbacks("DirectLogin.bookmarkletConfiguration", [
+ Clipperz.Async.collectResults("DirectLogin.bookmarkletConfiguration <inner results>", {
+ 'label': MochiKit.Base.method(this, 'label'),
+ 'configuration': MochiKit.Base.method(this, 'getValue', '')
+ }, {trace:false}),
+ function (someValues) {
+ var result;
+
+ if (someValues['configuration'] != null) {
+ var configuration;
+
+ configuration = {
+ 'page': {
+ 'title': someValues['label']
+ // 'favicon'
+ // 'url'
+ },
+ 'form': someValues['configuration']['formData'],
+ 'version': someValues['configuration']['bookmarkletVersion']
+ }
+
+ result = Clipperz.Base.formatJSON(configuration);
+ } else {
+ result = '';
+ }
+
+ return result;
+ }
+ ], {trace:false});
+
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setBookmarkletConfiguration': function (aValue) {
+ var bookmarkletConfiguration;
+
+ bookmarkletConfiguration = Clipperz.PM.DataModel.DirectLogin.checkBookmarkletConfiguration(aValue);
+
+ return Clipperz.Async.callbacks("DirectLogin.setBookmarkletConfiguration", [
+ MochiKit.Base.method(this, 'setValue', 'formData', bookmarkletConfiguration['form']),
+ MochiKit.Base.method(this, 'setValue', 'bookmarkletVersion', bookmarkletConfiguration['version']),
+
+ MochiKit.Base.method(this, 'favicon'),
+ Clipperz.Async.deferredIf("the favicon is not set", [
+ ], [
+ MochiKit.Base.method(this, 'faviconUrlWithBookmarkletConfiguration', bookmarkletConfiguration),
+ MochiKit.Base.method(this, 'setFavicon')
+ ]),
+
+ MochiKit.Base.method(this, 'updateInputsAfterChangingBookmarkletConfiguration'),
+ MochiKit.Base.method(this, 'updateFormValuesAfterChangingBookmarkletConfiguration'),
+ MochiKit.Base.method(this, 'updateBindingsAfterChangingBookmarkletConfiguration'),
+
+ MochiKit.Base.noop
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'formAttributes': function () {
+ return this.getValue('formData.attributes');
+ },
+
+ //=========================================================================
+
+ 'inputs': function () {
+ return Clipperz.Async.callbacks("DirectLogin.inputs", [
+ Clipperz.Async.deferredIf("this._inputs is defined", [
+ ], [
+ MochiKit.Base.method(this, 'updateInputsAfterChangingBookmarkletConfiguration')
+ ])
+ ], {trace:false}, this._inputs);
+ },
+
+ 'setInputWithFormDataConfiguration': function (aFormDataConfiguration) {
+ this._inputs = {};
+
+ if (aFormDataConfiguration != null) {
+ MochiKit.Iter.forEach(aFormDataConfiguration['inputs'], MochiKit.Base.bind(function (anInputData) {
+ var newInput;
+
+ newInput = new Clipperz.PM.DataModel.DirectLoginInput(anInputData);
+ this._inputs[newInput.name()] = newInput;
+ }, this));
+ }
+
+ return this._inputs;
+ },
+
+ 'updateInputsAfterChangingBookmarkletConfiguration': function () {
+ return Clipperz.Async.callbacks("DirectLogin.updateInputsAfterChangingBookmarkletConfiguration", [
+ MochiKit.Base.method(this, 'getValue', 'formData'),
+ MochiKit.Base.method(this, 'setInputWithFormDataConfiguration')
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'inputValues': function () {
+ return Clipperz.Async.callbacks("DirectLogin.inputValues", [
+ MochiKit.Base.method(this, 'inputs'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.partial(MochiKit.Base.method(this, 'inputValue'))),
+ Clipperz.Async.collectAll,
+ Clipperz.Base.mergeItems
+ ], {trace:false});
+ },
+
+ 'inputValue': function (anInput) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("DirectLogin.inputValue", {trace:false});
+
+ if (anInput.needsFormValue()) {
+ deferredResult.addMethod(this, 'formValues');
+ deferredResult.addCallback(MochiKit.Base.itemgetter(anInput.name()));
+ deferredResult.addMethodcaller('value');
+ } else if (anInput.needsBinding()) {
+ deferredResult.addMethod(this, 'bindings');
+ deferredResult.addCallback(MochiKit.Base.itemgetter(anInput.name()));
+ deferredResult.addMethodcaller('field');
+ deferredResult.addMethodcaller('value');
+ } else {
+ deferredResult.addCallback(MochiKit.Async.succeed, anInput.value());
+ }
+ deferredResult.addCallback(function (anActualValue) {
+ return [anInput.name(), anActualValue];
+ });
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'bindings': function () {
+ return Clipperz.Async.callbacks("DirectLogin.bindings", [
+ Clipperz.Async.deferredIf("this._bindings is defined", [
+ ], [
+ MochiKit.Base.method(this, 'updateBindingsAfterChangingBookmarkletConfiguration'),
+ MochiKit.Base.bind(function () { return this._bindings;}, this)
+ ])
+ ], {trace:false}, this._bindings);
+ },
+
+ 'bindFormFieldWithLabelToRecordFieldWithLabel': function (aFormFieldLabel, aRecordFieldLabel) {
+ return Clipperz.Async.callbacks("DirectLogin.bindFormFieldWithLabelToCardFieldWithLabel", [
+ Clipperz.Async.collectResults("DirectLogin.bindFormFieldWithLabelToCardFieldWithLabel - collect results", {
+ 'binding': [
+ MochiKit.Base.method(this, 'bindings'),
+ MochiKit.Base.itemgetter(aFormFieldLabel)
+ ],
+ 'field': [
+ MochiKit.Base.method(this.record(), 'fieldWithLabel', aRecordFieldLabel)
+ ]
+ }),
+ function (someValues) {
+ someValues['binding'].setField(someValues['field'])
+ }
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'bindingValues': function () {
+ return Clipperz.Async.callbacks("DirectLogin.bindingValues", [
+ Clipperz.Async.collectResults("DirectLogin.bindingValues [collectResults]", {
+ 'fieldValues': [
+ MochiKit.Base.method(this, 'record'),
+ MochiKit.Base.methodcaller('getFieldsValues')
+ ],
+ 'bindings': MochiKit.Base.method(this, 'bindings')
+ }, {trace:false}),
+ function (someData) {
+ var result;
+ var bindingKey;
+
+ result = {};
+ for (bindingKey in someData['bindings']) {
+ result[bindingKey] = someData['fieldValues'][someData['bindings'][bindingKey].fieldKey()]['value'];
+ }
+
+ return result;
+ }
+ ], {trace:false});
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'updateBindingsAfterChangingBookmarkletConfiguration': function () {
+ return Clipperz.Async.callbacks("DirectLogin.updateBindingsAfterChangingBookmarkletConfiguration", [
+ Clipperz.Async.collectResults("DirectLogin.updateBindingsAfterChangingBookmarkletConfiguration<collect results>", {
+ 'currentValues': MochiKit.Base.method(this, 'getValue', ''),
+ 'originalValues': MochiKit.Base.method(this, 'originalConfiguration'),
+ 'inputs': MochiKit.Base.method(this, 'inputs')
+ }, {trace:false}),
+ MochiKit.Base.bind(function (someValues) {
+ var availableBindingValues;
+ var inputRequiringBindingValues;
+ var newBindingValues;
+
+ if (MochiKit.Base.isUndefinedOrNull(someValues['originalValues']) || MochiKit.Base.isUndefinedOrNull(someValues['originalValues']['bindingData'])) {
+ availableBindingValues = {};
+ } else {
+ availableBindingValues = Clipperz.Base.deepClone(someValues['originalValues']['bindingData'])
+ }
+
+ if (someValues['currentValues'] != null) {
+ MochiKit.Base.update(availableBindingValues, someValues['currentValues']['bindingData']);
+ }
+
+ this._bindings = {};
+ newBindingValues = {}
+ MochiKit.Iter.forEach(MochiKit.Base.filter(MochiKit.Base.methodcaller('needsBinding'), MochiKit.Base.values(someValues['inputs'])), MochiKit.Base.bind(function (anInput) {
+ var newBinding;
+
+ newBindingValues[anInput.name()] = availableBindingValues[anInput.name()];
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(this, {
+ 'key': anInput.name(),
+ 'field': availableBindingValues[anInput.name()]
+ });
+
+ this._bindings[anInput.name()] = newBinding;
+ }, this))
+
+ return newBindingValues;
+
+/*
+ this._bindings = {};
+
+ if (someValues['currentValues'] != null) {
+ if (someValues['currentValues']['bindingData'] != null) {
+ var bindingKey;
+
+ for (bindingKey in someValues['currentValues']['bindingData']) {
+ var newBinding;
+
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(this, {
+ 'key': bindingKey,
+ 'field': someValues['currentValues']['bindingData'][bindingKey]
+ });
+ this._bindings[newBinding.key()] = newBinding;
+ }
+ } else if (someValues['currentValues']['legacyBindingData'] == null) {
+ var bindingKey;
+
+ for (bindingKey in someValues['currentValues']['legacyBindingData']) {
+ var newBinding;
+
+ newBinding = new Clipperz.PM.DataModel.DirectLoginBinding(this, {
+ 'key': bindingKey,
+ 'field': someValues['currentValues']['legacyBindingData'][bindingKey]
+ });
+ this._bindings[newBinding.key()] = newBinding;
+ }
+ } else {
+ WTF = TODO;
+ }
+ }
+
+ return this._bindings;
+*/
+ }, this),
+ MochiKit.Base.method(this, 'setValue', 'bindingData')
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'formValues': function () {
+ return Clipperz.Async.callbacks("DirectLogin.formValues", [
+ Clipperz.Async.deferredIf("this._formValues is defined", [
+ ], [
+ MochiKit.Base.method(this, 'updateFormValuesAfterChangingBookmarkletConfiguration'),
+ MochiKit.Base.bind(function () { return this._formValues;}, this)
+ ])
+ ], {trace:false}, this._formValues);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'updateFormValuesAfterChangingBookmarkletConfiguration': function () {
+ return Clipperz.Async.callbacks("DirectLogin.updateFormValuesAfterChangingBookmarkletConfiguration", [
+ Clipperz.Async.collectResults("DirectLogin.updateFormValuesAfterChangingBookmarkletConfiguration <collect results>", {
+ 'currentValues': MochiKit.Base.method(this, 'getValue', ''),
+ 'originalValues': MochiKit.Base.method(this, 'originalConfiguration'),
+ 'inputs': MochiKit.Base.method(this, 'inputs')
+ }, {trace:false}),
+ MochiKit.Base.bind(function (someValues) {
+ var availableFormValues;
+ var inputRequiringFormValues;
+ var newFormValues;
+
+ if (MochiKit.Base.isUndefinedOrNull(someValues['originalValues']) || MochiKit.Base.isUndefinedOrNull(someValues['originalValues']['formValues'])) {
+ availableFormValues = {};
+ } else {
+ availableFormValues = Clipperz.Base.deepClone(someValues['originalValues']['formValues'])
+ }
+
+ MochiKit.Base.update(availableFormValues, someValues['currentValues']['formValues']);
+
+ this._formValues = {};
+ newFormValues = {};
+ MochiKit.Iter.forEach(MochiKit.Base.filter(MochiKit.Base.methodcaller('needsFormValue'), MochiKit.Base.values(someValues['inputs'])), MochiKit.Base.bind(function (anInput) {
+ var newFormValue;
+ var fieldOptions;
+
+ fieldOptions = {
+ 'type': anInput.type(),
+ 'options': anInput.options()
+ };
+
+ newFormValues[anInput.name()] = availableFormValues[anInput.name()]
+ newFormValue = new Clipperz.PM.DataModel.DirectLoginFormValue(this, {
+ 'key': anInput.name(),
+ 'fieldOptions': fieldOptions,
+ 'value': availableFormValues[anInput.name()]
+ });
+
+ this._formValues[anInput.name()] = newFormValue;
+ }, this))
+
+ return newFormValues;
+ }, this),
+ MochiKit.Base.method(this, 'setValue', 'formValues')
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'retrieveIndexDataFunction': function () {
+ return this._retrieveIndexDataFunction;
+ },
+
+ 'getIndexDataForKey': function (aKey) {
+ return Clipperz.Async.callbacks("DirectLogin.getIndexDataForKey", [
+ MochiKit.Base.partial(this.retrieveIndexDataFunction(), this.reference()),
+ Clipperz.Async.deferredIf("DirectLogin.getIndexDataForKey - index data not null", [
+ MochiKit.Base.itemgetter(aKey)
+ ],[
+ MochiKit.Async.succeed
+ ])
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setIndexDataForKey': function (aKey, aValue) {
+ return Clipperz.Async.callbacks("DirectLogin.setValueForKey", [
+ MochiKit.Base.method(this, 'getIndexDataForKey', aKey),
+ MochiKit.Base.bind(function (anActualValue) {
+ var transientStateKey;
+
+ transientStateKey = 'original_' + aKey;
+ if (MochiKit.Base.isUndefinedOrNull(this.transientState()[transientStateKey])) {
+ if (anActualValue != aValue) {
+ this.transientState()[transientStateKey] = anActualValue;
+ }
+ } else if (this.transientState()[transientStateKey] == aValue) {
+ this.transientState()[transientStateKey] = null;
+ }
+ }, this),
+ MochiKit.Base.partial(this._setIndexDataFunction, this.reference(), aKey, aValue)
+ ], {trace:false})
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'setValueForKey': function (aKey, aValue) {
+ return Clipperz.Async.callbacks("DirectLogin.setValueForKey", [
+ MochiKit.Base.method(this, 'getIndexDataForKey', aKey),
+ MochiKit.Base.bind(function (anActualValue) {
+ var transientStateKey;
+
+ transientStateKey = 'original_' + aKey;
+ if (MochiKit.Base.isUndefinedOrNull(this.transientState()[transientStateKey])) {
+ if (anActualValue != aValue) {
+ this.transientState()[transientStateKey] = anActualValue;
+ }
+ } else if (this.transientState()[transientStateKey] == aValue) {
+ this.transientState()[transientStateKey] = null;
+ }
+ }, this),
+ MochiKit.Base.method(this, 'setIndexDataForKey', aKey, aValue)
+ ], {trace:false})
+ },
+*/
+ //=========================================================================
+/*
+ 'storedConfiguration': function () {
+ return this.record().getValue('directLogins' + '.' + this.reference());
+ },
+
+// 'setStoredConfiguration': function (aValue) {
+// return this.record().setValue('directLogins' + '.' + this.reference(), aValue);
+// },
+*/
+ //=========================================================================
+
+ 'hasPendingChanges': function () {
+ var result;
+ var deferredResult;
+
+ result = false;
+ result = result || this.isBrandNew();
+ result = result || (! MochiKit.Base.isUndefinedOrNull(this.transientState()['original_label']));
+ result = result || (! MochiKit.Base.isUndefinedOrNull(this.transientState()['original_favicon']));
+
+ if ((result == false) && (this.originalConfiguration() != null)) {
+ deferredResult = Clipperz.Async.callbacks("DirectLogin.hasPendingChanges", [
+ MochiKit.Base.method(this, 'serializedData'),
+ MochiKit.Base.bind(function (aCurrentConfiguration) {
+ var originalConfiguration;
+ var currentConfiguration;
+ var result;
+
+ originalConfiguration = this.originalConfiguration();
+ currentConfiguration = aCurrentConfiguration;
+
+ result = false;
+ result = result || (MochiKit.Base.compare(originalConfiguration['bookmarkletVersion'], currentConfiguration['bookmarkletVersion']) != 0);
+ result = result || (MochiKit.Base.compare(originalConfiguration['formData'], currentConfiguration['formData']) != 0);
+ result = result || (MochiKit.Base.compare(originalConfiguration['formValues'], currentConfiguration['formValues']) != 0);
+ result = result || (MochiKit.Base.compare(originalConfiguration['bindingData'], currentConfiguration['bindingData']) != 0);
+
+ return result;
+ }, this)
+ ], {trace:false});
+ } else {
+ deferredResult = MochiKit.Async.succeed(result);
+ }
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'revertChanges': function () {
+ var deferredResult;
+
+ if (this.transientState()['original_label'] != null) {
+ this.setLabel(this.transientState()['original_label']);
+ }
+
+ if (this.transientState()['original_favicon'] != null) {
+ this.setFavicon(this.transientState()['original_favicon']);
+ }
+
+ if (this.originalConfiguration() != null) {
+ deferredResult = this.setValue('', this.originalConfiguration());
+ } else {
+ deferredResult = MochiKit.Async.succeed();
+ }
+
+ this._inputs = null;
+ this._bindings = null;
+ this._formValues = null;
+
+ this.resetTransientState(false);
+
+/*
+ if (this.hasInitiatedObjectDataStore()) {
+ deferredResult = Clipperz.Async.callbacks("DirectLogin.revertChanges", [
+// MochiKit.Base.method(this.record(), 'setValue', 'directLogins' + '.' + this.reference(), this.originalState()),
+ MochiKit.Base.method(this, 'setValue', '', this.originalState()),
+ MochiKit.Base.method(this, 'resetObjectDataStore')
+ ], {trace:false})
+ } else {
+ deferredResult = MochiKit.Async.succeed();
+ }
+*/
+ return deferredResult;
+ },
+
+
+ //=========================================================================
+
+ 'transientState': function () {
+ if (this._transientState == null) {
+ this._transientState = {}
+ }
+
+ return this._transientState;
+ },
+
+ 'resetTransientState': function (isCommitting) {
+ this._transientState = null;
+ },
+
+ 'commitTransientState': function (isCommitting) {
+ this._transientState = null;
+ this._isBrandNew = false;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'originalConfiguration': function () {
+ return this.transientState()['original_configuration'];
+ },
+
+ 'setOriginalConfiguration': function (aConfiguration) {
+ this.transientState()['original_configuration'] = Clipperz.Base.deepClone(aConfiguration);
+ },
+
+ //=========================================================================
+
+ 'actualKey': function (aValueKey) {
+ var actualKey;
+
+ actualKey = 'directLogins' + '.' + this.reference();
+ if (aValueKey != '') {
+ actualKey = actualKey + '.' + aValueKey;
+ }
+
+ return actualKey;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getValue': function (aValueKey) {
+ return this.record().getValue(this.actualKey(aValueKey));
+ },
+
+ 'setValue': function (aValueKey, aValue) {
+// return this.record().setValue(this.actualKey(aValueKey), aValue);
+
+ return Clipperz.Async.callbacks("DirectLogin.setValue", [
+ MochiKit.Base.method(this, 'getValue', ''),
+ MochiKit.Base.bind(function (aValue) {
+ if (this.originalConfiguration() == null) {
+ this.setOriginalConfiguration(aValue);
+ }
+ }, this),
+// MochiKit.Base.method(this, 'originalConfiguration'),
+// Clipperz.Async.deferredIf("originalConfiguration has been set", [
+// ], [
+// MochiKit.Base.method(this, 'getValue', ''),
+// MochiKit.Base.method(this, 'setOriginalConfiguration')
+// ]),
+ MochiKit.Base.method(this.record(), 'setValue', this.actualKey(aValueKey), aValue)
+ ], {trace:false});
+ },
+
+ 'removeValue': function (aValueKey) {
+// return this.record().removeValue(this.actualKey(aValueKey));
+
+ return Clipperz.Async.callbacks("DirectLogin.setValue", [
+ MochiKit.Base.method(this, 'originalConfiguration'),
+ Clipperz.Async.deferredIf("originalConfiguration has been set", [
+ ], [
+ MochiKit.Base.method(this, 'getValue', ''),
+ MochiKit.Base.method(this, 'setOriginalConfiguration')
+ ]),
+ MochiKit.Base.method(this.record(), 'removeValue', this.actualKey(aValueKey))
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'content': function () {
+// return this.serializedData();
+// return MochiKit.Async.succeed(this);
+
+ var deferredResult;
+ var fieldValues;
+
+ fieldValues = {};
+ deferredResult = new Clipperz.Async.Deferred("DirectLogin.content", {trace:false});
+ deferredResult.addMethod(this, 'reference');
+ deferredResult.addCallback(function (aValue) { fieldValues['reference'] = aValue; });
+ deferredResult.addMethod(this, 'label');
+ deferredResult.addCallback(function (aValue) { fieldValues['label'] = aValue; });
+ deferredResult.addMethod(this, 'favicon');
+ deferredResult.addCallback(function (aValue) { fieldValues['favicon'] = aValue; });
+ deferredResult.addCallback(function () { return fieldValues; });
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'deleteAllCleanTextData': function () {
+ this._inputs = null;
+ this._bindings = null;
+ this._formValues = null;
+
+ this.resetTransientState();
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasAnyCleanTextData': function () {
+ var result;
+
+ result = false;
+
+ result = result || (this._inputs != null);
+ result = result || (this._bindings != null);
+ result = result || (this._formValues != null);
+ result = result || (MochiKit.Base.keys(this.transientState()).length != 0);
+
+ return MochiKit.Async.succeed(result);
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+//#############################################################################
+
+Clipperz.PM.DataModel.DirectLogin.exception = {
+ 'WrongBookmarkletConfiguration': new MochiKit.Base.NamedError("Clipperz.PM.DataModel.DirectLogin.exception.WrongBookmarkletConfiguration")
+};
+
+Clipperz.PM.DataModel.DirectLogin.checkBookmarkletConfiguration = function(aConfiguration) {
+ var configuration;
+
+ try {
+ configuration = Clipperz.Base.evalJSON(aConfiguration);
+// configuration = Clipperz.PM.BookmarkletProcessor.sanitizeBookmarkletConfiguration(configuration);
+
+ if (MochiKit.Base.isUndefinedOrNull(configuration['page']['title'])
+ || MochiKit.Base.isUndefinedOrNull(configuration['form']['attributes']['action'])
+// || MochiKit.Base.isUndefinedOrNull(configuration['form']['attributes']['method'])
+ || MochiKit.Base.isUndefinedOrNull(configuration['form']['inputs'])
+ || MochiKit.Base.isUndefinedOrNull(configuration['version'])
+ ) {
+ throw Clipperz.PM.DataModel.DirectLogin.exception.WrongBookmarkletConfiguration;
+ }
+
+// if (MochiKit.Base.isUndefinedOrNull(configuration['favicon'])) {
+// throw Clipperz.PM.DataModel.DirectLogin.exception.WrongBookmarkletConfiguration;
+// }
+
+ } catch (exception) {
+ throw exception;
+ }
+
+ return configuration;
+};
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginBinding.js b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginBinding.js
new file mode 100644
index 0000000..a8ebb97
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginBinding.js
@@ -0,0 +1,120 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+
+//#############################################################################
+
+Clipperz.PM.DataModel.DirectLoginBinding = function(aDirectLogin, args) {
+ args = args || {};
+
+ this._directLogin = aDirectLogin|| Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._key = args.key || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._fieldKey = args.field || /* this.directLogin().fieldWithName(args.fieldName).reference() || */ null;
+
+ return this;
+}
+
+Clipperz.PM.DataModel.DirectLoginBinding.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function() {
+ return "DirectLoginBinding (" + this.key() + ", " + this.fieldKey() + ")";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'directLogin': function () {
+ return this._directLogin;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'key': function() {
+ return this._key;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'fieldKey': function() {
+ return this._fieldKey;
+ },
+
+ 'setFieldKey': function(aValue) {
+ this._fieldKey = aValue;
+
+ return this.directLogin().setValue('bindingData' + '.' + this.key(), aValue);
+ },
+
+// 'fieldName': function() {
+// return this._fieldName;
+// },
+
+ //-------------------------------------------------------------------------
+
+ 'field': function() {
+ var deferredResult;
+
+ if (this.fieldKey() != null) {
+ deferredResult = Clipperz.Async.callbacks("DirectLoginBinding.field [1]", [
+ MochiKit.Base.method(this.directLogin().record(), 'fields'),
+ MochiKit.Base.itemgetter(this.fieldKey())
+ ], {trace:false});
+// } else if (this.fieldName() != null) {
+// WTF = TODO;
+// result = this.directLogin().record().fieldWithName(this.fieldName());
+//
+// this.setFieldKey(result.key());
+ } else {
+ deferredResult = MochiKit.Async.succeed(null);
+ }
+
+ return deferredResult;
+ },
+
+ 'setField': function (aField) {
+ this.setFieldKey(aField.reference());
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'fieldValue': function () {
+ return Clipperz.Async.callbacks("DirectLoginBinding.fieldValue", [
+ MochiKit.Base.method('field'),
+ MochiKit.Base.methodcaller('value')
+ ], {trace:false});
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'serializedData': function() {
+ return this.fieldKey();
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginFormValue.js b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginFormValue.js
new file mode 100644
index 0000000..2429f88
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginFormValue.js
@@ -0,0 +1,101 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+
+//#############################################################################
+
+Clipperz.PM.DataModel.DirectLoginFormValue = function(aDirectLogin, args) {
+ args = args || {};
+
+ this._directLogin = aDirectLogin|| Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._key = args.key || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._fieldOptions = args.fieldOptions || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._value = args.value || null;
+
+ return this;
+}
+
+Clipperz.PM.DataModel.DirectLoginFormValue.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function() {
+ return "DirectLoginFormValue (" + this.key() + ", " + this.value() + ")";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'directLogin': function () {
+ return this._directLogin;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'key': function() {
+ return this._key;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'fieldOptions': function() {
+ return this._fieldOptions;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'type': function () {
+ return this.fieldOptions()['type'];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'value': function() {
+ var result;
+
+ result = this._value;
+
+// if ((result == null) && (this.type() == 'checkbox')) {
+// result = false;
+// };
+
+ return result;
+ },
+
+ 'setValue': function (aValue) {
+ this._value = aValue;
+ return this.directLogin().setValue('formValues' + '.' + this.key(), aValue);
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'serializedData': function() {
+ return this.value();
+ },
+*/
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginInput.js b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginInput.js
new file mode 100644
index 0000000..d9995fc
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/DirectLoginInput.js
@@ -0,0 +1,192 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+//#############################################################################
+
+Clipperz.PM.DataModel.DirectLoginInput = function(args) {
+ this._args = args;
+
+ return this;
+}
+
+Clipperz.PM.DataModel.DirectLoginInput.prototype = MochiKit.Base.update(null, {
+
+ 'args': function() {
+ return this._args;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'name': function() {
+ return this.args()['name'];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'type': function() {
+ var result;
+
+ result = this.args()['type'];
+
+ if (result != null) {
+ result = result.toLowerCase();
+ }
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'options': function() {
+ return this.args()['options'];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'value': function() {
+ return this.args()['value'];
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'formConfiguration': function(someFormValues, someBindings, someFields) {
+ var result;
+
+ if (this.shouldSetValue()) {
+ switch (this.type()) {
+ case 'select':
+ var currentValue;
+ var options;
+
+// currentValue = this.directLogin()._configuration['formValues'][this.name()];
+ currentValue = someFormValues[this.name()];
+ options = this.args()['options'];
+
+ result = MochiKit.DOM.SELECT({name:this.name()},
+ MochiKit.Base.map(function(anOption) {
+ var options;
+
+ options = {value:anOption['value']};
+ if (currentValue == anOption['value']) {
+ options.selected = true;
+ }
+
+ return MochiKit.DOM.OPTION(options, anOption['label'])
+ }, options)
+ )
+ break;
+ case 'checkbox':
+ var options;
+
+ options = {type:'checkbox', name: this.name()};
+// if (this.directLogin()._configuration['formValues'][this.name()] == true) {
+ if (someFormValues[this.name()] == true) {
+ options['checked'] = true;
+ };
+
+ result = MochiKit.DOM.INPUT(options, null);
+ break;
+ case 'radio':
+ var currentName;
+ var currentValue;
+ var options;
+
+ currentName = this.name();
+// currentValue = this.directLogin()._configuration['formValues'][this.name()];
+ currentValue = someFormValues[this.name()];
+ options = this.args()['options'];
+
+ result = MochiKit.DOM.DIV(null,
+ MochiKit.Base.map(function(anOption) {
+ var options;
+ var isChecked;
+ var inputNode;
+ var divNode;
+
+ options = {type:'radio', name:currentName, value:anOption['value']}
+ isChecked = (currentValue == anOption['value']);
+ if (isChecked) {
+ options.checked = true;
+ }
+
+ if (Clipperz_IEisBroken == true) {
+ var checkedValue;
+
+ checkedValue = (isChecked ? " CHECKED" : "");
+ inputNode = MochiKit.DOM.currentDocument().createElement("<INPUT TYPE='RADIO' NAME='" + currentName + "' VALUE='" + anOption['value'] + "'" + checkedValue + ">");
+ } else {
+ inputNode = MochiKit.DOM.INPUT(options, anOption['value']);
+ }
+ divNode = MochiKit.DOM.DIV(null, inputNode);
+
+ return divNode;
+ }, options)
+ );
+ break;
+ }
+ } else {
+ var binding;
+// binding = this.directLogin().bindings()[this.name()];
+ binding = someBindings[this.name()];
+
+ result = MochiKit.DOM.INPUT({
+ type:((this.type() != 'password') ? this.type() : 'text'),
+ name:this.name(),
+// value:((binding != null)? binding.field().value() : this.value())
+ value:((binding != null)? someFields[binding.fieldKey()]['value'] : this.value())
+// value:((binding != null)? someFields[binding.fieldKey()].value() : this.value())
+ }, null);
+ }
+
+ return result;
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'needsFormValue': function() {
+ var type;
+ var result;
+
+ type = this.type();
+ result = ((type == 'checkbox') || (type == 'radio') || (type == 'select'));
+
+ return result;
+ },
+
+ 'needsBinding': function() {
+ var type;
+ var result;
+
+ type = this.type();
+ result = ((type == 'text') || (type == 'password'));
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/EncryptedRemoteObject.js b/frontend/delta/js/Clipperz/PM/DataModel/EncryptedRemoteObject.js
new file mode 100644
index 0000000..1aa7a52
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/EncryptedRemoteObject.js
@@ -0,0 +1,542 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.KeyValueObjectStore) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.EncryptedRemoteObject depends on Clipperz.KeyValueObjectStore!";
+}
+
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+Clipperz.PM.DataModel.EncryptedRemoteObject = function(args) {
+ args = args || {};
+
+ this._name = args.name || null;
+ this._reference = args.reference || Clipperz.PM.Crypto.randomKey();
+ this._isBrandNew = ((args.reference == null) && (args.remoteData == null));
+
+ if ((this._isBrandNew == false) && (args['retrieveKeyFunction'] == null)) {
+ Clipperz.Base.exception.raise('MandatoryParameter');
+ } else {
+ this._retrieveKeyFunction = args['retrieveKeyFunction'];
+ }
+
+ this._retrieveRemoteDataFunction = args.retrieveRemoteDataFunction || null;
+ this._remoteData = args.remoteData || null;
+// this._remoteData = args.remoteData ? Clipperz.Base.deepClone(args.remoteData) : null;
+ if ((!this._isBrandNew) && ((this._retrieveRemoteDataFunction == null) && (this._remoteData == null))) {
+ Clipperz.Base.exception.raise('MandatoryParameter');
+ }
+
+
+ this._encryptedDataKeypath = args.encryptedDataKeypath || 'data'; //Clipperz.Base.exception.raise('MandatoryParameter');
+ this._encryptedVersionKeypath = args.encryptedVersionKeypath || 'version'; //Clipperz.Base.exception.raise('MandatoryParameter');
+
+
+ this._transientState = null;
+ this._deferredLocks = {};
+
+ if (this._isBrandNew == true) {
+ this._objectDataStore = new Clipperz.KeyValueObjectStore(/*{'name':'EncryptedRemoteObject.objectDataStore [1]'}*/);
+ } else {
+ this._objectDataStore = null;
+ }
+
+ return this;
+}
+
+//
+// Basic data workflow
+// =======================
+//
+// getRemoteData
+// unpackRemoteData
+// getDecryptData [encryptedDataKeypath, encryptedVersionKeypath]
+// unpackData
+//
+//
+// ?? packData
+// ?? encryptDataWithKey
+// ?? packRemoteData [encryptedDataKeypath (?), encryptedVersionKeypath (?)]
+//
+
+Clipperz.PM.DataModel.EncryptedRemoteObject.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function () {
+ return "Clipperz.PM.DataModel.EncryptedRemoteObject" + (this.name() != null ? " - " + this.name() : "");
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'name': function () {
+ return this._name;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'reference': function () {
+ return this._reference;
+ },
+
+ 'setReference': function (aValue) {
+ this._reference = aValue;
+
+ return this._reference;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'transientState': function () {
+ if (this._transientState == null) {
+ this._transientState = new Clipperz.KeyValueObjectStore(/*{'name':'EncryptedRemoteObject.transientState [2]'}*/);
+ }
+
+ return this._transientState;
+ },
+
+ 'resetTransientState': function (isCommitting) {
+ if (this._transientState != null) {
+ this._transientState.removeAllData();
+ }
+
+ this._transientState = null;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isBrandNew': function () {
+ return this._isBrandNew;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getKey': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('key');
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.getKey", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addMethod(
+ this.decryptedDataStore(),
+ 'deferredGetOrSet',
+ 'decryptionKey',
+ MochiKit.Base.partial(this.retrieveKeyFunction(), this.reference())
+ );
+ deferredResult.releaseLock(deferredLock);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+ 'retrieveKeyFunction': function () {
+ return this._retrieveKeyFunction;
+ },
+
+ 'setRetrieveKeyFunction': function (aFunction) {
+ this._retrieveKeyFunction = aFunction;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasLoadedRemoteData': function () {
+ return (this._remoteData != null);
+ },
+
+ 'getRemoteData': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('remoteData');
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObjects.getRemoteData", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._remoteData != null) {
+ innerDeferredResult = MochiKit.Async.succeed(this._remoteData);
+ } else {
+ innerDeferredResult = Clipperz.Async.callbacks("EncryptedRemoteObjects.getRemoteData <inner deferred>", [
+ MochiKit.Base.partial(this.retrieveRemoteDataFunction(), this.reference()),
+ MochiKit.Base.method(this, 'unpackRemoteData'),
+ MochiKit.Base.bind(function (someData) {
+ this._remoteData = someData;
+ return this._remoteData;
+ }, this)
+ ], {trace:false});
+ }
+
+ return innerDeferredResult;
+ }, this))
+ deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ deferredResult.releaseLock(deferredLock);
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'unpackRemoteData': function (someData) {
+ return MochiKit.Async.succeed(someData);
+ },
+
+ //.........................................................................
+
+ 'packRemoteData': function (someData) {
+ var result;
+
+ result = {
+ 'reference': this.reference(),
+ 'data': someData,
+ 'version': Clipperz.PM.Crypto.encryptingFunctions.currentVersion
+ };
+
+ return MochiKit.Async.succeed(result);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'retrieveRemoteDataFunction': function () {
+ return this._retrieveRemoteDataFunction;
+ },
+
+ 'setRetrieveRemoteDataFunction': function (aFunction) {
+ this._retrieveRemoteDataFunction = aFunction;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'decryptedDataStore': function () {
+ if (this._decryptedDataStore == null) {
+ this._decryptedDataStore = new Clipperz.KeyValueObjectStore(/*{'name':'EncryptedRemoteObject.decryptedDataStore [3]'}*/);
+ };
+
+ return this._decryptedDataStore;
+ },
+
+ //.........................................................................
+
+ 'getDecryptedData': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('decryptedData');
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.getDecryptedData", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addMethod(this, 'decryptedDataStore');
+ deferredResult.addCallback(MochiKit.Base.methodcaller('deferredGetOrSet', 'decryptedData', MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ innerDeferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.getDecryptedData <inner deferred>", {trace:false});
+
+ innerDeferredResult.addMethod(this, 'getRemoteData');
+ innerDeferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ innerDeferredResult.collectResults({
+ 'key': MochiKit.Base.method(this, 'getKey'),
+ 'value': MochiKit.Base.itemgetter(this._encryptedDataKeypath),
+ 'version': MochiKit.Base.itemgetter(this._encryptedVersionKeypath)
+ });
+
+ innerDeferredResult.addCallback(Clipperz.PM.Crypto.deferredDecrypt);
+ innerDeferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ innerDeferredResult.addMethod(this, 'unpackData');
+ innerDeferredResult.callback();
+
+ return innerDeferredResult;
+ }, this)));
+ deferredResult.releaseLock(deferredLock);
+ deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setValue': function(aKey, aValue) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.setValue", {trace:false});
+ deferredResult.addMethod(this, '_getObjectDataStore');
+ deferredResult.addCallback(MochiKit.Base.methodcaller('setValue', aKey, aValue));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //.........................................................................
+
+ 'getValue': function (aKey) {
+ return Clipperz.Async.callbacks("EncryptedRemoteObject.getValue", [
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('getValue', aKey)
+ ], {trace:false});
+ },
+
+ //.........................................................................
+
+ 'removeValue': function (aKey) {
+ return Clipperz.Async.callbacks("EncryptedRemoteObject.removeValue", [
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('removeValue', aKey)
+ ], {trace:false});
+ },
+
+ //.........................................................................
+
+ 'values': function () {
+ return Clipperz.Async.callbacks("EncryptedRemoteObject.values", [
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values')
+ ], {trace:false});
+ },
+
+ 'setValues': function (someValues) {
+ return Clipperz.Async.callbacks("EncryptedRemoteObject.values", [
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('setValues', someValues)
+ ], {trace:false});
+ },
+
+ //.........................................................................
+
+ '_getObjectDataStore': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('objectDataStore');
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject._getObjectDataStore", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._objectDataStore == null) {
+ this._objectDataStore = new Clipperz.KeyValueObjectStore(/*{'name':'EncryptedRemoteObject.objectDataStore [4]'}*/);
+
+ innerDeferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject._getObjectDataStore <inner deferred>", {trace:false});
+ innerDeferredResult.addMethod(this, 'getDecryptedData');
+ innerDeferredResult.addMethod(this._objectDataStore, 'initWithValues');
+ innerDeferredResult.callback();
+ } else {
+ innerDeferredResult = MochiKit.Async.succeed(this._objectDataStore);
+ }
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(deferredLock);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ 'hasInitiatedObjectDataStore': function () {
+ return (this._objectDataStore != null);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getDeferredLockForKey': function (aKey) {
+ var result;
+
+ result = this._deferredLocks[aKey];
+
+ if (typeof(result) == 'undefined') {
+ result = new MochiKit.Async.DeferredLock();
+ this._deferredLocks[aKey] = result;
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'unpackData': function (someData) { // ++
+ return someData;
+ },
+
+ 'packData': function (someData) { // ++
+ return someData;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasPendingChanges': function () {
+ var deferredResult;
+ var tempObj = this;
+
+ if (this.isBrandNew()) {
+// deferredResult = MochiKit.Async.succeed(true);
+ deferredResult = this.hasPendingChangesWhenBrandNew();
+ } else if (this.hasInitiatedObjectDataStore()) {
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.hasPendingChanges", {trace:false});
+ deferredResult.collectResults({
+ 'decryptedData': [
+ MochiKit.Base.method(this, 'getDecryptedData'),
+ Clipperz.Base.serializeJSON
+ ],
+ 'objectData': [
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values'),
+ Clipperz.Base.serializeJSON
+ ]
+ });
+ deferredResult.addCallback(function (someValues) {
+ return (someValues['decryptedData'] != someValues['objectData']);
+ });
+ deferredResult.callback();
+ } else {
+ deferredResult = MochiKit.Async.succeed(false);
+ }
+
+ return deferredResult;
+ },
+
+ 'hasPendingChangesWhenBrandNew': function () {
+ return MochiKit.Async.succeed(true);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'commitTransientState': function () {
+ var deferredResult;
+
+// if (this.transientState().getValue('__prepareRemoteData') == true) {
+ if (this.transientState().getValue('packedRemoteData') != null) {
+ deferredResult = Clipperz.Async.callbacks("EncryptedRemoteObject.commitTransientState - prepareRemoteData", [
+ MochiKit.Base.bind(function (someData) {
+ this._remoteData = this.transientState().getValue('packedRemoteData');
+ }, this),
+
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values'),
+ Clipperz.Base.deepClone,
+ MochiKit.Base.method(this.decryptedDataStore(), 'setValue', 'decryptedData'),
+
+ MochiKit.Base.method(this, 'resetTransientState', true)
+ ], {trace:false});
+
+ } else {
+ deferredResult = Clipperz.Async.callbacks("EncryptedRemoteObject.commitTransientState - NO prepareRemoteData", [
+ MochiKit.Base.method(this, 'resetTransientState', true)
+ ], {trace:false});
+ }
+
+ this._isBrandNew = false;
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'revertChanges': function () {
+ if (this.hasInitiatedObjectDataStore()) {
+ this._objectDataStore.removeAllData();
+ this._objectDataStore = null;
+ }
+ this.resetTransientState(false);
+
+ return MochiKit.Async.succeed();
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'deleteAllCleanTextData': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.deleteAllCleanTextData", {trace:false});
+
+ deferredResult.addMethod(this, 'resetTransientState', false);
+
+ deferredResult.acquireLock(this.getDeferredLockForKey('decryptedData'));
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ if (this._decryptedDataStore != null) {
+ this._decryptedDataStore.removeAllData();
+ }
+ }, this));
+ deferredResult.releaseLock(this.getDeferredLockForKey('decryptedData'));
+
+ deferredResult.acquireLock(this.getDeferredLockForKey('objectDataStore'));
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ if (this._objectDataStore != null) {
+ this._objectDataStore.removeAllData();
+ this._objectDataStore = null;
+ }
+ }, this));
+ deferredResult.releaseLock(this.getDeferredLockForKey('objectDataStore'));
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //.........................................................................
+
+ 'hasAnyCleanTextData': function () {
+ var result;
+
+ result = false;
+
+ result = result || (! this.decryptedDataStore().isEmpty());
+ result = result || (! this.transientState().isEmpty());
+ if (this.hasInitiatedObjectDataStore()) {
+ result = result || (! this._objectDataStore.isEmpty());
+ }
+
+ return MochiKit.Async.succeed(result);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'prepareRemoteDataWithKey': function (aKey) {
+ return Clipperz.Async.callbacks("EncryptedRemoteObject.prepareRemoteDataWithKey", [
+// MochiKit.Base.method(this.transientState(), 'setValue', '__prepareRemoteData', true),
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values'),
+ MochiKit.Base.method(this, 'packData'),
+ function (someData) {
+ return Clipperz.PM.Crypto.deferredEncrypt({
+ 'key': aKey,
+ 'value': someData,
+ 'version': Clipperz.PM.Crypto.encryptingFunctions.currentVersion
+ })
+ },
+ MochiKit.Base.method(this, 'packRemoteData'),
+ MochiKit.Base.method(this.transientState(), 'setValue', 'packedRemoteData'),
+ function (someData) {
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ return someData;
+ }
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/OneTimePassword.js b/frontend/delta/js/Clipperz/PM/DataModel/OneTimePassword.js
new file mode 100644
index 0000000..fbca1ff
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/OneTimePassword.js
@@ -0,0 +1,350 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+
+//#############################################################################
+
+Clipperz.PM.DataModel.OneTimePassword = function(args) {
+ args = args || {};
+
+// this._user = args['user'];
+ this._reference = args['reference'] || Clipperz.PM.Crypto.randomKey();
+ this._password = args['password'];
+ this._passwordValue = Clipperz.PM.DataModel.OneTimePassword.normalizedOneTimePassword(args['password']);
+ this._creationDate = args['created'] ? Clipperz.PM.Date.parseDateWithUTCFormat(args['created']) : new Date();
+ this._usageDate = args['used'] ? Clipperz.PM.Date.parseDateWithUTCFormat(args['used']) : null;
+
+ this._status = args['status'] || 'ACTIVE'; // 'REQUESTED', 'USED', 'DISABLED'
+ this._connectionInfo= null;
+
+ this._key = null;
+ this._keyChecksum = null;
+
+ return this;
+}
+
+Clipperz.PM.DataModel.OneTimePassword.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function() {
+ return "Clipperz.PM.DataModel.OneTimePassword";
+ },
+/*
+ //-------------------------------------------------------------------------
+
+ 'user': function() {
+ return this._user;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'password': function() {
+ return this._password;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'passwordValue': function() {
+ return this._passwordValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'creationDate': function() {
+ return this._creationDate;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'reference': function() {
+ return this._reference;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'key': function() {
+ if (this._key == null) {
+ this._key = Clipperz.PM.DataModel.OneTimePassword.computeKeyWithUsernameAndPassword(this.user().username(), this.passwordValue());
+ }
+
+ return this._key;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'keyChecksum': function() {
+ if (this._keyChecksum == null) {
+ this._keyChecksum = Clipperz.PM.DataModel.OneTimePassword.computeKeyChecksumWithUsernameAndPassword(this.user().username(), this.passwordValue());
+ }
+
+ return this._keyChecksum;
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'status': function() {
+ return this._status;
+ },
+
+ 'setStatus': function(aValue) {
+ this._status = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'serializedData': function() {
+ var result;
+
+ result = {
+ 'password': this.password(),
+ 'created': this.creationDate() ? Clipperz.PM.Date.formatDateWithUTCFormat(this.creationDate()) : null,
+ 'used': this.usageDate() ? Clipperz.PM.Date.formatDateWithUTCFormat(this.usageDate()) : null,
+ 'status': this.status()
+ };
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'packedPassphrase': function() {
+ var result;
+ var packedPassphrase;
+ var encodedPassphrase;
+ var prefixPadding;
+ var suffixPadding;
+ var getRandomBytes;
+
+ getRandomBytes = MochiKit.Base.method(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'getRandomBytes');
+
+ encodedPassphrase = new Clipperz.ByteArray(this.user().passphrase()).toBase64String();
+//Clipperz.logDebug("--- encodedPassphrase.length: " + encodedPassphrase.length);
+ prefixPadding = getRandomBytes(getRandomBytes(1).byteAtIndex(0)).toBase64String();
+//Clipperz.logDebug("--- prefixPadding.length: " + prefixPadding.length);
+ suffixPadding = getRandomBytes((500 - prefixPadding.length - encodedPassphrase.length) * 6 / 8).toBase64String();
+//Clipperz.logDebug("--- suffixPadding.length: " + suffixPadding.length);
+//Clipperz.logDebug("--- total.length: " + (prefixPadding.length + encodedPassphrase.length + suffixPadding.length));
+
+ packedPassphrase = {
+ 'prefix': prefixPadding,
+ 'passphrase': encodedPassphrase,
+ 'suffix': suffixPadding
+ };
+
+// result = Clipperz.Base.serializeJSON(packedPassphrase);
+ result = packedPassphrase;
+//Clipperz.logDebug("===== OTP packedPassprase: [" + result.length + "]" + result);
+//Clipperz.logDebug("<<< OneTimePassword.packedPassphrase");
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'encryptedPackedPassphrase': function() {
+ return Clipperz.PM.Crypto.deferredEncryptWithCurrentVersion(this.passwordValue(), this.packedPassphrase())
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'encryptedData': function() {
+ var deferredResult;
+ var result;
+
+//Clipperz.logDebug(">>> OneTimePassword.encryptedData");
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - id: " + this.reference());
+ result = {
+ 'reference': this.reference(),
+ 'key': this.key(),
+ 'keyChecksum': this.keyChecksum(),
+ 'data': "",
+ 'version': Clipperz.PM.Crypto.encryptingFunctions.currentVersion
+ }
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - 2: " + Clipperz.Base.serializeJSON(result));
+ deferredResult = new MochiKit.Async.Deferred();
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - 3");
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.encryptedData - 1: " + res); return res;});
+//# deferredResult.addCallback(Clipperz.PM.Crypto.deferredEncryptWithCurrentVersion, this.passwordValue(), this.packedPassphrase());
+ deferredResult.addCallback(MochiKit.Base.method(this, 'encryptedPackedPassphrase'));
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - 4");
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.encryptedData - 2: [" + res.length + "]" + res); return res;});
+ deferredResult.addCallback(function(aResult, res) {
+ aResult['data'] = res;
+ return aResult;
+ }, result);
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - 5");
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.encryptedData - 3: " + Clipperz.Base.serializeJSON(res)); return res;});
+ deferredResult.callback();
+//Clipperz.logDebug("--- OneTimePassword.encryptedData - 6");
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'saveChanges': function() {
+ var deferredResult;
+ var result;
+
+//Clipperz.logDebug(">>> OneTimePassword.saveChanges");
+ result = {};
+ deferredResult = new MochiKit.Async.Deferred();
+
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'updatedProgressState', 'saveOTP_encryptUserData');
+ deferredResult.addCallback(MochiKit.Base.method(this.user(), 'encryptedData'));
+ deferredResult.addCallback(function(aResult, res) {
+ aResult['user'] = res;
+ return aResult;
+ }, result);
+
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'updatedProgressState', 'saveOTP_encryptOTPData');
+ deferredResult.addCallback(MochiKit.Base.method(this, 'encryptedData'));
+ deferredResult.addCallback(function(aResult, res) {
+ aResult['oneTimePassword'] = res;
+ return aResult;
+ }, result);
+
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'updatedProgressState', 'saveOTP_sendingData');
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.saveChanges - 1: " + Clipperz.Base.serializeJSON(res)); return res;});
+ deferredResult.addCallback(MochiKit.Base.method(this.user().connection(), 'message'), 'addNewOneTimePassword');
+
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'updatedProgressState', 'saveOTP_updatingInterface');
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.saveChanges - 2: " + res); return res;});
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'notify', 'OTPUpdated');
+ deferredResult.addCallback(Clipperz.NotificationCenter.deferredNotification, this, 'oneTimePassword_saveChanges_done', null);
+//deferredResult.addBoth(function(res) {Clipperz.logDebug("OneTimePassword.saveChanges - 2: " + res); return res;});
+ deferredResult.callback();
+//Clipperz.logDebug("<<< OneTimePassword.saveChanges");
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'usageDate': function() {
+ return this._usageDate;
+ },
+
+ 'setUsageDate': function(aValue) {
+ this._usageDate = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'connectionInfo': function() {
+ return this._connectionInfo;
+ },
+
+ 'setConnectionInfo': function(aValue) {
+ this._connectionInfo = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isExpired': function() {
+ return (this.usageDate() != null);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'updateStatusWithValues': function(someValues) {
+ var result;
+
+ result = false;
+
+ if (someValues['status'] != this.status()) {
+ result = true;
+ }
+
+ this.setStatus(someValues['status']);
+ this.setUsageDate(Clipperz.PM.Date.parseDateWithUTCFormat(someValues['requestDate']));
+ this.setConnectionInfo(someValues['connection']);
+
+ return result;
+ },
+*/
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
+//#############################################################################
+
+Clipperz.PM.DataModel.OneTimePassword.computeKeyWithUsernameAndPassword = function(anUsername, aPassword) {
+ return Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(aPassword)).toHexString().substring(2);
+}
+
+Clipperz.PM.DataModel.OneTimePassword.computeKeyChecksumWithUsernameAndPassword = function(anUsername, aPassword) {
+ return Clipperz.Crypto.SHA.sha_d256(new Clipperz.ByteArray(anUsername + aPassword)).toHexString().substring(2);
+}
+
+//=============================================================================
+
+Clipperz.PM.DataModel.OneTimePassword.isValidOneTimePasswordValue = function(aPassword) {
+ var result;
+
+// "yaxx k7ww - f8y6 tqz5 - 58b6 th44 - 9cwv q0fg"
+ if (aPassword.replace(/[\s\-]/g, '').length == 32) {
+ try {
+ var passwordByteArray;
+
+ passwordByteArray = new Clipperz.ByteArray();
+ passwordByteArray.appendBase32String(aPassword);
+
+ result = true;
+ } catch(exception) {
+ result = false;
+ }
+ } else {
+ result = false;
+ }
+
+ return result;
+}
+
+//=============================================================================
+
+Clipperz.PM.DataModel.OneTimePassword.normalizedOneTimePassword = function(aPassword) {
+ var result;
+
+ if (aPassword.replace(/[\s\-]/g, '').length == 32) {
+ try {
+ var passwordByteArray;
+
+ passwordByteArray = new Clipperz.ByteArray();
+ passwordByteArray.appendBase32String(aPassword);
+
+ result = passwordByteArray.toBase64String();
+ } catch(exception) {
+ result = aPassword;
+ }
+ } else {
+ result = aPassword;
+ }
+
+ return result;
+}
+
+//#############################################################################
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.Field.js b/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.Field.js
new file mode 100644
index 0000000..01e7196
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.Field.js
@@ -0,0 +1,186 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.Record.Version) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.Record.Version.Field depends on Clipperz.PM.DataModel.Record.Version!";
+}
+
+Clipperz.PM.DataModel.Record.Version.Field = function(args) {
+ Clipperz.PM.DataModel.Record.Version.Field.superclass.constructor.apply(this, arguments);
+
+ this._recordVersion = args.recordVersion || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._reference = args.reference || Clipperz.PM.Crypto.randomKey();
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.Record.Version.Field, Object, {
+
+ 'toString': function() {
+ return "Record.Version.Field (" + this.reference() + ")";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'recordVersion': function () {
+ return this._recordVersion;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'reference': function () {
+ return this._reference;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getItem': function (aKey) {
+ return Clipperz.Async.callbacks("Clipperz.PM.DataModel.Record.Version.Field.getItem", [
+ MochiKit.Base.method(this, 'recordVersion'),
+ MochiKit.Base.methodcaller('getValue', 'fields' + '.' + this.reference() + '.' + aKey)
+ ], {trace:false});
+ },
+
+ 'setItem': function (aKey, aValue) {
+ return Clipperz.Async.callbacks("Clipperz.PM.DataModel.Record.Version.Field.getItem", [
+ MochiKit.Base.method(this, 'recordVersion'),
+ MochiKit.Base.methodcaller('setValue', 'fields' + '.' + this.reference() + '.' + aKey, aValue)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'label': function () {
+ return this.getItem('label');
+ },
+
+ 'setLabel': function (aValue) {
+ return this.setItem('label', aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'value': function () {
+ return this.getItem('value');
+ },
+
+ 'setValue': function (aValue) {
+ return this.setItem('value', aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'actionType': function () {
+ return Clipperz.Async.callbacks("Clipperz.PM.DataModel.Record.Version.Field.actionType", [
+ Clipperz.Async.collectResults("Clipperz.PM.DataModel.Record.Version.Field.actionType [collect results]", {
+ 'isHidden': MochiKit.Base.method(this, 'isHidden'),
+ 'value': MochiKit.Base.method(this, 'value')
+ }, {trace:false}),
+ function (someValues) {
+ var result; // 'NONE', 'URL', 'EMAIL', 'PASSWORD'
+
+ result = 'NONE';
+
+ if (someValues['isHidden']) {
+ result = 'PASSWORD';
+ } else if (Clipperz.Base.isUrl(someValues['value'])) {
+ result = 'URL'
+ } else if (Clipperz.Base.isEmail(someValues['value'])) {
+ result = 'EMAIL'
+ };
+
+ return result;
+ }
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isHidden': function () {
+ return this.getItem('hidden');
+ },
+
+ 'setIsHidden': function (aValue) {
+ return this.setItem('hidden', aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isEmpty': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.Version.Field.isEmpty", {trace:false});
+
+ deferredResult.collectResults({
+ 'label': [
+ MochiKit.Base.method(this, 'label'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, '')
+ ],
+ 'value': [
+ MochiKit.Base.method(this, 'value'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, '')
+ ],
+ 'isHidden': [
+ MochiKit.Base.method(this, 'isHidden'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, false)
+ ]
+ });
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(function(someValues) {
+ return MochiKit.Iter.every(someValues, MochiKit.Base.operator.identity);
+ });
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'content': function () {
+ var deferredResult;
+ var fieldValues;
+
+ fieldValues = {};
+ deferredResult = new Clipperz.Async.Deferred("Record.Version.Field.content", {trace:false});
+ deferredResult.addMethod(this, 'reference');
+ deferredResult.addCallback(function (aValue) { fieldValues['reference'] = aValue; });
+ deferredResult.addMethod(this, 'label');
+ deferredResult.addCallback(function (aValue) { fieldValues['label'] = aValue; });
+ deferredResult.addMethod(this, 'value');
+ deferredResult.addCallback(function (aValue) { fieldValues['value'] = aValue; });
+ deferredResult.addMethod(this, 'actionType');
+ deferredResult.addCallback(function (aValue) { fieldValues['actionType'] = aValue; });
+ deferredResult.addMethod(this, 'isHidden');
+ deferredResult.addCallback(function (aValue) { fieldValues['isHidden'] = aValue; });
+ deferredResult.addCallback(function () { return fieldValues; });
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js b/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js
new file mode 100644
index 0000000..87b319c
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/Record.Version.js
@@ -0,0 +1,328 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.Record) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.Record.Version depends on Clipperz.PM.DataModel.Record!";
+}
+
+Clipperz.PM.DataModel.Record.Version = function(args) {
+ Clipperz.PM.DataModel.Record.Version.superclass.constructor.apply(this, arguments);
+
+ this._getVersionFunction = args.getVersion || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._fields = null;
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.Record.Version, Clipperz.PM.DataModel.EncryptedRemoteObject, {
+
+ 'toString': function() {
+ return "Record.Version (" + this.reference() + ")";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'reference': function () {
+ return this._reference;
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'hasPendingChanges': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.Version.hasPendingChanges", {trace:false});
+ deferredResult.addCallback(MochiKit.Base.bind(Clipperz.PM.DataModel.Record.Version.superclass.hasPendingChanges, this));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+*/
+ //-------------------------------------------------------------------------
+
+
+ 'hasPendingChangesWhenBrandNew': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.Version.hasPendingChangesWhenBrandNew", {trace:false});
+ deferredResult.addMethod(this, 'fields');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.methodcaller('isEmpty'))
+ deferredResult.addCallback(Clipperz.Async.collectAll);
+ deferredResult.addCallback(function(someValues) {
+ return MochiKit.Iter.every(someValues, MochiKit.Base.operator.identity);
+ });
+ deferredResult.addCallback(MochiKit.Base.operator.lognot)
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'commitTransientState': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.Version.commitTransientState", {trace:false});
+ deferredResult.addCallback(MochiKit.Base.bind(Clipperz.PM.DataModel.Record.Version.superclass.commitTransientState, this));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'unpackData': function (someData) { // ++
+ var result;
+
+ result = someData;
+ if ((someData['fields'] != null) && (someData['fields'] instanceof Array)) {
+ var fields;
+ var i,c;
+
+ fields = someData['fields'];
+ delete someData['fields'];
+
+ someData['fields'] = {};
+ c = fields.length;
+ for (i=0; i<c; i++) {
+ someData['fields'][i] = fields[i];
+ }
+ }
+
+
+
+ return result;
+ },
+
+ //=========================================================================
+
+ 'fields': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('fields');
+
+ deferredResult = new Clipperz.Async.Deferred("Record.Version.fields", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._fields == null) {
+ innerDeferredResult = new Clipperz.Async.Deferred("Record.Version.fields <inner deferred>", {trace:false});
+ innerDeferredResult.addMethod(this, 'getValue', 'fields');
+ innerDeferredResult.addCallback(MochiKit.Base.bind(function (someObjectData) {
+ var reference;
+
+ this._fields = {};
+
+ for (reference in someObjectData) {
+ var recordVersionField;
+
+ recordVersionField = new Clipperz.PM.DataModel.Record.Version.Field({
+ 'recordVersion': this,
+ 'reference': reference
+ });
+
+ this._fields[reference] = recordVersionField;
+ }
+
+ return this._fields;
+ }, this));
+ innerDeferredResult.callback();
+ } else {
+ innerDeferredResult = MochiKit.Async.succeed(this._fields);
+ }
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(deferredLock);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getFieldsValues': function () {
+ return this.getValue('fields');
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'addField': function (someParameters) {
+ var newField;
+
+ newField = new Clipperz.PM.DataModel.Record.Version.Field({recordVersion:this});
+
+ return Clipperz.Async.callbacks("Record.Version.addField", [
+ MochiKit.Base.method(this, 'fields'),
+
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values'),
+ Clipperz.Base.serializeJSON,
+
+ MochiKit.Base.bind(function () { this._fields[newField.reference()] = newField; }, this),
+ MochiKit.Base.method(newField, 'setLabel', someParameters['label']),
+ MochiKit.Base.method(newField, 'setValue', someParameters['value']),
+ MochiKit.Base.method(newField, 'setIsHidden', someParameters['isHidden']),
+
+ MochiKit.Base.method(this, '_getObjectDataStore'),
+ MochiKit.Base.methodcaller('values'),
+ Clipperz.Base.serializeJSON,
+
+ MochiKit.Base.partial(MochiKit.Async.succeed, newField)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'removeField': function (aField) {
+ return Clipperz.Async.callbacks("Record.Version.removeField", [
+ MochiKit.Base.method(this, 'fields'),
+ MochiKit.Base.bind(function () { delete this._fields[aField.reference()]; }, this),
+ MochiKit.Base.method(this, 'removeValue', 'fields' + '.' + aField.reference())
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'sortFieldReference': function (someSortedFieldReferences) {
+
+
+
+ },
+*/
+ //=========================================================================
+/*
+ 'directLogins': function () {
+ return MochiKit.Base.values(this._directLogins);
+ },
+
+ 'addDirectLogin': function (aDirectLogin) {
+ this._directLogins[aDirectLogin.reference()] = aDirectLogin;
+ },
+*/
+
+ //=========================================================================
+/*
+ 'updateValues': function (anotherVersion) {
+ return Clipperz.Async.callbacks("Record.Version.updateValue", [
+ MochiKit.Base.partial(MochiKit.Async.succeed, this)
+ ], {trace:false});
+ },
+*/
+ //=========================================================================
+
+ 'setRemoteData': function (aValue) {
+ this._remoteData = aValue;
+
+ return aValue;
+ },
+
+ //=========================================================================
+
+ 'getVersionFunction': function () {
+ return this._getVersionFunction;
+ },
+
+ 'previousVersion': function () {
+ return Clipperz.Async.callbacks("Record.Versions.previousVersion", [
+ MochiKit.Base.method(this, 'previousVersionReference'),
+ this.getVersionFunction()
+ ], {trace:false});
+ },
+
+ 'previousVersionReference': function () {
+ return this.getValue('previousVersionReference');
+ },
+
+ 'previousVersionKey': function () {
+// TODO: this value i encrypted on its own. So it can not be saved in the main objectStore!!!
+ return this.getValue('previousVersionKey');
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setPreviousVersionReferenceAndKey': function (aVersionObjectAndKey) {
+// this._previousVersion = anotherVersion;
+ return Clipperz.Async.callbacks("Record.Version.setPreviousVersion", [
+ MochiKit.Base.method(this, 'setValue', 'previousVersionReference', aVersionObjectAndKey['reference']),
+ MochiKit.Base.method(this, 'setValue', 'previousVersionKey', aVersionObjectAndKey['key'])
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'revertChanges': function () {
+ this.setReference(this.transientState()['originalReference']);
+ Clipperz.PM.DataModel.Record.Version.superclass.revertChanges.apply(this, arguments);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'prepareRemoteDataWithKey': function (aKey) {
+ var deferredResult;
+ var result;
+
+ result = {};
+
+ deferredResult = new Clipperz.Async.Deferred("Record.Version.prepareRemoteDataWithKey", {trace:false});
+ if (this.isBrandNew() == false) {
+ this.transientState()['originalReference'] = this.reference();
+
+ deferredResult.collectResults({
+ 'key': MochiKit.Base.partial(MochiKit.Async.succeed, aKey),
+ 'value': MochiKit.Base.method(this, 'getKey'),
+ 'version': MochiKit.Base.partial(MochiKit.Async.succeed, Clipperz.PM.Crypto.encryptingFunctions.currentVersion)
+ });
+ deferredResult.addCallback(Clipperz.PM.Crypto.deferredEncrypt);
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'previousVersionKey');
+ } else {
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'previousVersionKey', Clipperz.PM.Crypto.nullValue);
+ }
+ deferredResult.addCallback(MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.prepareRemoteDataWithKey, this, aKey));
+ deferredResult.addCallback(MochiKit.Base.update, result);
+ deferredResult.addMethod(this, 'setRemoteData');
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+/*
+ 'deleteAllCleanTextData': function () {
+ return Clipperz.PM.DataModel.Record.Version.superclass.deleteAllCleanTextData.apply(this, arguments);
+ },
+
+ 'hasAnyCleanTextData': function () {
+ return Clipperz.PM.DataModel.Record.Version.superclass.hasAnyCleanTextData.apply(this, arguments);
+ },
+*/
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/Record.js b/frontend/delta/js/Clipperz/PM/DataModel/Record.js
new file mode 100644
index 0000000..379872a
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/Record.js
@@ -0,0 +1,891 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+
+Clipperz.PM.DataModel.Record = function(args) {
+ Clipperz.PM.DataModel.Record.superclass.constructor.apply(this, arguments);
+
+ this._updateDate = (args.updateDate ? Clipperz.PM.Date.parse(args.updateDate) : Clipperz.Base.exception.raise('MandatoryParameter'));
+
+ this._retrieveIndexDataFunction = args.retrieveIndexDataFunction || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._updateIndexDataFunction = args.updateIndexDataFunction || Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._retrieveDirectLoginIndexDataFunction = args.retrieveDirectLoginIndexDataFunction || null;
+ this._setDirectLoginIndexDataFunction = args.setDirectLoginIndexDataFunction || null;
+ this._removeDirectLoginIndexDataFunction = args.removeDirectLoginIndexDataFunction || null;
+
+ this._createNewDirectLoginFunction = args.createNewDirectLoginFunction || null;
+
+ this._directLogins = {};
+
+ this._versions = {};
+
+ this._currentRecordVersion = null;
+ if (this.isBrandNew()) {
+ var newVersion;
+
+ this.setNotes('');
+ newVersion = new Clipperz.PM.DataModel.Record.Version({
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getVersionKey'),
+ 'getVersion': MochiKit.Base.method(this, 'getVersion')
+
+ });
+ this._versions[newVersion.reference()] = newVersion;
+ this._currentVersionReference = newVersion.reference();
+// this.setLabel('');
+ }
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.EncryptedRemoteObject, {
+
+ 'toString': function() {
+ return "Record (" + this.reference() + ")";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'reference': function () {
+ return this._reference;
+ },
+
+ //=========================================================================
+
+ 'getIndexData': function () {
+ return this._retrieveIndexDataFunction(this.reference());
+ },
+
+ //.........................................................................
+
+ 'getIndexDataForKey': function (aKey) {
+ return Clipperz.Async.callbacks("Record.getIndexDataForKey", [
+ MochiKit.Base.method(this, 'getIndexData'),
+ MochiKit.Base.itemgetter(aKey)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setIndexDataForKey': function (aKey, aValue) {
+// return this._updateIndexDataFunction(this.reference(), aKey, aValue);
+
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Record.setIndexDataForKey", {trace:false});
+ deferredResult.addMethod(this, 'getIndexDataForKey', aKey);
+ deferredResult.addCallback(MochiKit.Base.bind(function (aCurrentValue) {
+ var result;
+ var originalValue;
+
+ originalValue = this.transientState().getValue('originalValues.indexData.' + aKey);
+ if (originalValue == null) {
+ originalValue = this.transientState().setValue('originalValues.indexData.' + aKey, aCurrentValue);
+ }
+
+ if (aCurrentValue != aValue) {
+ if (originalValue != aValue) {
+ this.transientState().setValue('hasPendingChanges.indexData.' + aKey, true);
+ } else {
+ this.transientState().setValue('hasPendingChanges.indexData.' + aKey, false);
+ }
+
+ result = this._updateIndexDataFunction(this.reference(), aKey, aValue);
+ } else {
+ result = MochiKit.Async.succeed(aValue);
+ }
+
+ return result;
+ }, this));
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+/*
+ 'key': function () {
+ return this.getIndexDataForKey('key');
+ },
+*/
+ //=========================================================================
+
+ 'label': function () {
+ return this.getIndexDataForKey('label');
+ },
+
+ //.........................................................................
+
+ 'setLabel': function (aValue) {
+ return this.setIndexDataForKey('label', aValue);
+ },
+
+ //=========================================================================
+
+ 'headerNotes': function () {
+ return this.getIndexDataForKey('notes');
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'notes': function () {
+ return Clipperz.Async.callbacks("Record.notes", [
+ MochiKit.Base.method(this, 'headerNotes'),
+ MochiKit.Base.bind(function (someHeaderNotes) {
+ var result;
+
+ if ((someHeaderNotes == null) || (typeof(someHeaderNotes) == 'undefined')) {
+ result = this.getValue('notes');
+ } else {
+ result = MochiKit.Async.succeed(someHeaderNotes);
+ }
+
+ return result;
+ }, this)
+ ], {trace:false});
+ },
+
+ //.........................................................................
+
+ 'setNotes': function (aValue) {
+ return this.setValue('notes', aValue);
+ },
+
+ //=========================================================================
+
+ 'updateDate': function () {
+ return MochiKit.Async.succeed(this._updateDate);
+ },
+
+ //=========================================================================
+
+ 'favicon': function () {
+ var result;
+ var directLogins;
+
+ directLogins = MochiKit.Base.values(this.directLogins());
+ if (directLogins.length > 0) {
+ result = directLogins[0].favicon();
+// } else if (/* is there an URL to use for searching a favicon */){
+ } else {
+ result = null; // MochiKit.Async.succeed(Clipperz.PM.Strings['defaultFaviconUrl']);
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'searchableContent': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Record.searchableContent", {trace:false});
+
+ deferredResult.collectResults({
+ 'recordLabel': MochiKit.Base.method(this, 'label'),
+ 'directLoginLabels': [
+ MochiKit.Base.method(this, 'directLoginReferences'),
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.itemgetter('label'))
+ ]
+ })
+ deferredResult.addCallback(function (someValues) {
+ return someValues['recordLabel'] + ' ' + someValues['directLoginLabels'].join(' ');
+ });
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isMatching': function (aRegExp) {
+ return Clipperz.Async.callbacks("deferredFilterFunction", [
+ MochiKit.Base.method(this, 'searchableContent'),
+ MochiKit.Base.method(aRegExp, 'test'),
+ function (doesItMatch) {
+ var result;
+
+ if (doesItMatch) {
+ result = MochiKit.Async.succeed('match');
+ } else {
+ result = MochiKit.Async.fail('miss');
+ }
+
+ return result;
+ }
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'content': function () {
+ var deferredResult;
+ var result;
+
+ result = {
+ 'fields': [],
+ 'directLogins': []
+ };
+
+ deferredResult = new Clipperz.Async.Deferred("Record.content", {trace:false});
+ deferredResult.addMethod(this, 'reference');
+ deferredResult.addCallback(function (aValue) { result['reference'] = aValue; });
+ deferredResult.addMethod(this, 'label');
+ deferredResult.addCallback(function (aValue) { result['title'] = aValue; });
+ deferredResult.addMethod(this, 'notes');
+ deferredResult.addCallback(function (aValue) { result['notes'] = aValue; });
+
+ deferredResult.addMethod(this, 'fields');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.methodcaller('content'));
+ deferredResult.addCallback(Clipperz.Async.collectAll);
+ deferredResult.addCallback(MochiKit.Base.map, function (aValue) { result['fields'].push(aValue); });
+
+ deferredResult.addMethod(this, 'directLogins');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.methodcaller('content'));
+ deferredResult.addCallback(Clipperz.Async.collectAll);
+ deferredResult.addCallback(MochiKit.Base.map, function (aValue) { result['directLogins'].push(aValue); });
+ deferredResult.addCallback(function () { return result; });
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'directLogins': function () {
+ return this._directLogins;
+ },
+
+ 'addDirectLogin': function (aDirectLogin) {
+ this._directLogins[aDirectLogin.reference()] = aDirectLogin;
+ },
+
+ 'directLoginWithReference': function (aDirectLoginReference) {
+ return this._directLogins[aDirectLoginReference];
+ },
+
+ 'createNewDirectLoginFunction': function () {
+ return this._createNewDirectLoginFunction;
+ },
+
+ 'saveOriginalDirectLoginStatusToTransientState': function () {
+ if (this.transientState().getValue('directLogins') == null) {
+// this.transientState().setValue('directLogins', this._directLogins)
+ MochiKit.Iter.forEach(MochiKit.Base.keys(this._directLogins), MochiKit.Base.bind(function(aKey) {
+ this.transientState().setValue('directLogins' + '.' + aKey, this._directLogins[aKey])
+ }, this))
+ }
+ },
+
+ 'createNewDirectLogin': function () {
+ this.saveOriginalDirectLoginStatusToTransientState();
+
+ return this.createNewDirectLoginFunction()(this);
+ },
+
+ 'removeDirectLogin': function(aDirectLogin) {
+ this.saveOriginalDirectLoginStatusToTransientState();
+
+ return Clipperz.Async.callbacks("Record.removeDirectLogin", [
+ MochiKit.Base.method(this, 'removeValue', 'directLogins' + '.' + aDirectLogin.reference()),
+ MochiKit.Base.bind(function () {
+ delete this._directLogins[aDirectLogin.reference()]
+ }, this)
+ ], {trace:false});
+
+ },
+
+ 'directLoginReferences': function () {
+ var result;
+
+ result = Clipperz.Async.callbacks("Record.directLoginReferences", [
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ function (someDirectLogins) {
+ var result;
+ var i,c;
+
+ result = [];
+ c = someDirectLogins.length;
+ for (i=0; i<c; i++) {
+ result.push(Clipperz.Async.collectResults("Record.directLoginReferences - collectResults", {
+ '_rowObject': MochiKit.Async.succeed,
+ '_reference': MochiKit.Base.methodcaller('reference'),
+ 'label': MochiKit.Base.methodcaller('label'),
+ 'favicon': MochiKit.Base.methodcaller('favicon')
+ }, {trace:false})(someDirectLogins[i]));
+ };
+
+ return result;
+ },
+ Clipperz.Async.collectAll
+ ], {trace:false});
+
+ return result;
+ },
+
+ //=========================================================================
+
+ 'unpackRemoteData': function (someData) {
+ var result;
+
+/*
+ this._currentRecordVersion = new Clipperz.PM.DataModel.Record.Version({
+ 'reference': someData['currentVersion']['reference'],
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getCurrentRecordVersionKey'),
+ 'remoteData': someData['currentVersion'],
+ });
+*/
+ var versionKey;
+
+ for (versionKey in someData['versions']) {
+ this._versions[versionKey] = new Clipperz.PM.DataModel.Record.Version({
+ 'reference': versionKey,
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getVersionKey'),
+ 'remoteData': someData['versions'][versionKey],
+ 'getVersion': MochiKit.Base.method(this, 'getVersion')
+ })
+ }
+
+// this._currentVersionReference = someData['currentVersion']['reference'];
+ this._currentVersionReference = someData['currentVersion'];
+
+ result = Clipperz.PM.DataModel.Record.superclass.unpackRemoteData.apply(this, arguments);
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'unpackData': function (someData) {
+ var result;
+
+ result = Clipperz.PM.DataModel.Record.superclass.unpackData.apply(this, arguments);
+
+ if (MochiKit.Base.isUndefinedOrNull(result['notes'])) {
+ result['notes'] = ''
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'prepareRemoteDataWithKey': function (aKey) {
+ var deferredResult;
+ var newVersionKey;
+ var result;
+
+ newVersionKey = Clipperz.PM.Crypto.randomKey();
+ result = {};
+
+ deferredResult = new Clipperz.Async.Deferred("Record.prepareRemoteDataWithKey", {trace:false});
+ deferredResult.addCallbackList([
+ Clipperz.Async.collectResults("Record.prepareRemoteDataWithKey - collect results", {
+ 'isBrandNew': MochiKit.Base.method(this, 'isBrandNew'),
+ 'versionHasPendingChanges': [
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.methodcaller('hasPendingChanges')
+ MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'hasPendingChanges')
+ ]
+ }),
+ Clipperz.Async.or,
+
+ Clipperz.Async.deferredIf("Current Version has pending changes", [
+ MochiKit.Base.method(this, 'createNewRecordVersion'),
+ MochiKit.Base.methodcaller('prepareRemoteDataWithKey', newVersionKey),
+ MochiKit.Base.partial(Clipperz.Async.setItem, result, 'currentRecordVersion'),
+ MochiKit.Base.method(this, 'setCurrentRecordVersionKey', newVersionKey)
+ ], []),
+
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.prepareRemoteDataWithKey, this, aKey),
+ MochiKit.Base.partial(Clipperz.Async.setItem, result, 'record'),
+
+ MochiKit.Base.partial(MochiKit.Async.succeed, result)
+ ]);
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'fields': function () {
+ return this.invokeCurrentRecordVersionMethod('fields');
+ },
+
+ 'addField': function (someParameters) {
+ return this.invokeCurrentRecordVersionMethod('addField', someParameters);
+ },
+
+ 'removeField': function (someParameters) {
+ return this.invokeCurrentRecordVersionMethod('removeField', someParameters);
+ },
+
+// 'sortFieldReference': function (someSortedFieldReferences) {
+// return this.invokeCurrentRecordVersionMethod('sortFieldReference', someSortedFieldReferences);
+// },
+
+ 'getFieldsValues': function () {
+ return this.invokeCurrentRecordVersionMethod('getFieldsValues');
+ },
+
+ 'fieldWithLabel': function (aLabel) {
+ return Clipperz.Async.callbacks("Record.fieldWithLabel", [
+ MochiKit.Base.method(this, 'fields'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(Clipperz.Async.deferredFilter, function (aField) {
+ return Clipperz.Async.callbacks("Record.fieldWithLabel - check field label", [
+ MochiKit.Base.methodcaller('label'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, aLabel)
+ ], {trace:false}, aField);
+ }),
+ function (someFilteredResults) {
+ var result;
+
+ switch (someFilteredResults.length) {
+ case 0:
+ result = null;
+ break;
+ case 1:
+ result = someFilteredResults[0];
+ break;
+ default:
+ WTF = TODO;
+ break;
+ }
+
+ return result;
+ }
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'getVersion': function (aVersionReference) {
+ return Clipperz.Async.callbacks("Record.getVersion", [
+ MochiKit.Base.method(this, 'getVersions'),
+ MochiKit.Base.itemgetter(aVersionReference)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getVersionKey': function (aVersionReference) {
+ var deferredResult;
+ var transientStateKey;
+
+ transientStateKey = 'versionKeys' + '.' + aVersionReference;
+ if (this.transientState().getValue(transientStateKey) != null) {
+ deferredResult = MochiKit.Async.succeed(this.transientState().getValue(transientStateKey));
+ } else {
+ deferredResult = Clipperz.Async.callbacks("Record.getVersionKey", [
+ MochiKit.Base.method(this, 'getVersions'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, aVersionReference, this.currentVersionReference()),
+ Clipperz.Async.deferredIf("getVersionKey for current version", [
+ MochiKit.Base.method(this, 'getCurrentRecordVersionKey'),
+ MochiKit.Base.method(this.transientState(), 'setValue', transientStateKey)
+ ],[
+ MochiKit.Async.fail
+ ])
+ ], {trace:false});
+ }
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'versions': function () {
+ return this._versions;
+ },
+
+ 'getVersions': function () {
+ return Clipperz.Async.callbacks("Record.versions", [
+ MochiKit.Base.method(this, 'getValue', 'fakeKey, just to trigger unpackRemoteData'),
+ MochiKit.Base.bind(function () { return this._versions; }, this)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getCurrentRecordVersion': function () {
+ return Clipperz.Async.callbacks("Record.getCurrentRecordVersion", [
+// MochiKit.Base.method(this, 'getValue', 'fakeKey, just to trigger unpackRemoteData'),
+// MochiKit.Base.bind(function () { return this._currentRecordVersion; }, this)
+
+ MochiKit.Base.method(this, 'versions'),
+ MochiKit.Base.itemgetter(this.currentVersionReference()),
+ Clipperz.Async.deferredIf("The current version is available", [
+ MochiKit.Async.succeed
+ ], [
+ MochiKit.Base.method(this, 'getVersions'),
+ MochiKit.Base.bind(function (someVersions) { return someVersions[this.currentVersionReference()]}, this)
+ ])
+ ], {trace:false});
+ },
+
+ 'setCurrentRecordVersion': function (aRecordVersion) {
+ this._currentVersionReference = aRecordVersion.reference();
+ },
+
+ //.........................................................................
+
+ 'currentVersionReference': function () {
+ return this._currentVersionReference;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'createNewRecordVersion': function () {
+ var deferredResult;
+
+ if (this.isBrandNew()) {
+ deferredResult = this.getCurrentRecordVersion();
+ } else {
+ var newVersion;
+
+ newVersion = new Clipperz.PM.DataModel.Record.Version({
+ // 'reference': versionKey,
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getVersionKey'),
+// 'remoteData': {},
+ 'getVersion': MochiKit.Base.method(this, 'getVersion')
+ })
+ this._versions[newVersion.reference()] = newVersion;
+
+ deferredResult = Clipperz.Async.callbacks("Record.createNewRecordVersion", [
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.methodcaller('values'),
+ MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'values'),
+ MochiKit.Base.method(newVersion, 'setValues'),
+
+ Clipperz.Async.collectResults("Record.createNewRecordVersion [collect results]", {
+ 'reference': MochiKit.Base.method(this, 'currentVersionReference'),
+ 'key': MochiKit.Base.method(this, 'getCurrentRecordVersionKey')
+ }, {trace:false}),
+ MochiKit.Base.method(newVersion, 'setPreviousVersionReferenceAndKey'),
+
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.method(this, 'revertChanges'),
+ MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'revertChanges'),
+
+ MochiKit.Base.method(this, 'setCurrentRecordVersion', newVersion),
+ MochiKit.Base.partial(MochiKit.Async.succeed, newVersion)
+ ], {trace:false});
+ }
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getCurrentRecordVersionKey': function () {
+ return Clipperz.Async.callbacks("Record.getCurrentRecordVersionKey", [
+ MochiKit.Base.method(this, 'getValue', 'currentVersionKey'),
+ Clipperz.Async.deferredIf("currentVersionKey is NOT null", [
+ MochiKit.Async.succeed
+ ], [
+ MochiKit.Base.method(this, 'getKey')
+ ])
+ ], {trace:false});
+ },
+
+ 'setCurrentRecordVersionKey': function (aValue) {
+ // TODO: triple check this method!
+ return Clipperz.Async.callbacks("Record.setCurrentRecordVersionKey", [
+ MochiKit.Base.method(this, 'setValue', 'currentVersionKey', aValue)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'invokeCurrentRecordVersionMethod': function (aMethodName, someValues) {
+ return Clipperz.Async.callbacks("Record.invokeCurrentRecordVersionMethod", [
+ MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+ MochiKit.Base.methodcaller(aMethodName, someValues)
+ ], {trace:false});
+ },
+
+
+ 'lazilyinvokeCurrentRecordVersionMethod': function (aMethodName, someValues, defaultResult) {
+ return Clipperz.Async.callbacks("Record.lazilyinvokeCurrentRecordVersionMethod", [
+ MochiKit.Base.method(this, 'currentVersionReference'),
+ Clipperz.Async.deferredIf("versions has been loaded", [
+ MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+ MochiKit.Base.methodcaller(aMethodName, someValues),
+ ], [
+ MochiKit.Base.partial(MochiKit.Async.succeed, defaultResult),
+ ])
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'hasPendingChanges': function () {
+ var deferredResult;
+
+ if (this.hasInitiatedObjectDataStore()) {
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.hasPendingChanges", {trace:false});
+ deferredResult.collectResults({
+ 'super': MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.hasPendingChanges, this),
+ 'currentVersion': [
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.methodcaller('hasPendingChanges')
+ MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'hasPendingChanges')
+ ],
+ 'directLogins': [
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
+ Clipperz.Async.collectAll,
+ Clipperz.Async.or
+// function(someValues) {
+// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+// }
+ ]
+ });
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.bind(function(someValues) {
+ var result;
+ result = MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+
+ if ((result == false) && (this.isBrandNew() == false)) {
+ result = MochiKit.Iter.some(MochiKit.Base.values(this.transientState().getValue('hasPendingChanges.indexData')), MochiKit.Base.operator.identity);
+ }
+
+ return result;
+ }, this));
+
+ deferredResult.callback();
+ } else {
+ deferredResult = Clipperz.Async.callbacks("Recrod.hasPendingChanges [hasInitiatedObjectDataStore == false]", [
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
+ Clipperz.Async.collectAll,
+ Clipperz.Async.or
+// function(someValues) {
+// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+// }
+ ], {trace:false})
+ }
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasPendingChangesWhenBrandNew': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.hasPendingChangesWhenBrandNew", {trace:false});
+ deferredResult.collectResults({
+ 'label': [
+ MochiKit.Base.method(this, 'label'),
+ MochiKit.Base.partial(MochiKit.Base.operator.ne, '')
+ ],
+ 'notes': [
+ MochiKit.Base.method(this, 'notes'),
+ MochiKit.Base.partial(MochiKit.Base.operator.ne, '')
+ ]
+ });
+// deferredResult.addCallback(MochiKit.Base.values);
+// deferredResult.addCallback(function(someValues) {
+// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+// });
+ deferredResult.addCallback(Clipperz.Async.or);
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isBrandNewWithNoPendingChanges': function () {
+ var deferredResult;
+
+ if (this.isBrandNew() == false) {
+ deferredResult = MochiKit.Async.succeed(false);
+ } else {
+ deferredResult = Clipperz.Async.callbacks("Record.isBrandNewWithNoPendingChanges", [
+ MochiKit.Base.method(this, 'hasPendingChanges'),
+ MochiKit.Base.operator.lognot
+ ], {trace:false});
+ }
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'revertChanges': function () {
+ var deferredResult;
+
+ if (this.isBrandNew() == false) {
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.revertChanges", {trace:false});
+ deferredResult.addMethod(this, 'hasPendingChanges');
+ deferredResult.addIf([
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.methodcaller('revertChanges'),
+ MochiKit.Base.method(this,'invokeCurrentRecordVersionMethod', 'revertChanges'),
+
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('revertChanges')),
+
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.revertChanges, this)
+ ], [
+ MochiKit.Async.succeed
+ ]);
+ deferredResult.callback();
+ } else {
+// this.deleteAllCleanTextData();
+ deferredResult = MochiKit.Async.succeed();
+ }
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'resetTransientState': function (isCommitting) {
+// if ((isCommitting == false) && (this.transientState().getValue('directLogins') != null)) {
+// this._directLogins = this.transientState().getValue('directLogins');
+// }
+
+ return Clipperz.Async.callbacks("Record.resetTransientState", [
+//- MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+//- MochiKit.Base.methodcaller('resetTransientState'),
+// MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'resetTransientState'),
+ MochiKit.Base.method(this, 'lazilyinvokeCurrentRecordVersionMethod', 'resetTransientState'),
+
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('resetTransientState')),
+
+ MochiKit.Base.bind(function () {
+ if ((isCommitting == false) && (this.transientState().getValue('directLogins') != null)) {
+ this._directLogins = this.transientState().getValue('directLogins');
+ }
+ }, this),
+
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.resetTransientState, this, isCommitting)
+ ], {trace:false})
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'commitTransientState': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.commitTransientState", {trace:false});
+ deferredResult.addMethod(this, 'hasPendingChanges');
+ deferredResult.addIf([
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.commitTransientState, this),
+// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
+// MochiKit.Base.methodcaller('commitTransientState'),
+ MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'commitTransientState'),
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('commitTransientState'))
+ ], [
+ MochiKit.Async.succeed
+ ]);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'retrieveDirectLoginIndexDataFunction': function () {
+ return this._retrieveDirectLoginIndexDataFunction;
+ },
+
+ 'setDirectLoginIndexDataFunction': function () {
+ return this._setDirectLoginIndexDataFunction;
+ },
+
+ 'removeDirectLoginIndexDataFunction': function () {
+ return this._removeDirectLoginIndexDataFunction;
+ },
+
+ //=========================================================================
+
+ 'deleteAllCleanTextData': function () {
+// return Clipperz.PM.DataModel.Record.superclass.deleteAllCleanTextData.apply(this, arguments);
+
+ return Clipperz.Async.callbacks("Record.deleteAllCleanTextData", [
+ MochiKit.Base.method(this, 'versions'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
+
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
+
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.deleteAllCleanTextData, this)
+ ], {trace:false});
+ },
+
+ 'hasAnyCleanTextData': function () {
+// return Clipperz.PM.DataModel.Record.superclass.hasAnyCleanTextData.apply(this, arguments);
+
+ return Clipperz.Async.callbacks("Record.hasAnyCleanTextData", [
+ Clipperz.Async.collectResults("Record.hasAnyCleanTextData [collect results]", {
+ 'versions': [
+ MochiKit.Base.method(this, 'versions'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
+ Clipperz.Async.collectAll
+ ],
+ 'directLogins': [
+ MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.values,
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
+ Clipperz.Async.collectAll
+ ],
+ 'super': [
+ MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.hasAnyCleanTextData, this)
+ ]
+ }, {trace:false}),
+ Clipperz.Async.or
+ ])
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Legacy.js b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Legacy.js
new file mode 100644
index 0000000..cda5a41
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Legacy.js
@@ -0,0 +1,182 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.User.Header.Legacy depends on Clipperz.PM.DataModel.User!";
+}
+
+if (typeof(Clipperz.PM.DataModel.User.Header) == 'undefined') { Clipperz.PM.DataModel.User.Header = {}; }
+
+Clipperz.PM.DataModel.User.Header.Legacy = function(args) {
+// args = args || {};
+ Clipperz.PM.DataModel.User.Header.Legacy.superclass.constructor.apply(this, arguments);
+
+ this._retrieveRecordDetailFunction = args.retrieveRecordDetailFunction || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._records = null;
+// this._directLogins = null;
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.Legacy, Clipperz.PM.DataModel.EncryptedRemoteObject, {
+
+ 'toString': function() {
+ return "Clipperz.PM.DataModel.User.Header.Legacy";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'retrieveRecordDetailFunction': function () {
+ return this._retrieveRecordDetailFunction;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getRecordKey': function (aRecordReference) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.Legacy.getRecordKey", {trace:false});
+ deferredResult.addMethod(this, 'getRecordIndexData');
+ deferredResult.addCallback(MochiKit.Base.itemgetter('key'))
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'getRecordIndexData': function (aRecordReference) {
+ return this.getValue('records.' + aRecordReference);
+ },
+
+ 'updateRecordIndexData': function (aRecordReference, aKey, aValue) {
+ return this.setValue('records.' + aRecordReference + "." + aKey, aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getDirectLoginIndexData': function (aDirectLoginReference) {
+ return this.getValue('directLogins.' + aDirectLoginReference);
+ },
+
+ 'setDirectLoginIndexData': function (aDirectLoginReference, aKey, aValue) {
+ return this.setValue('directLogins.' + aDirectLoginReference + '.' + aKey, aValue);
+ },
+
+ 'removeDirectLoginIndexData': function (aDirectLoginReference) {
+ return this.removeValue('directLogins.' + aDirectLoginReference);
+ },
+
+ //=========================================================================
+
+ 'records': function () {
+ var deferredResult;
+ var deferredLock;
+
+ deferredLock = this.getDeferredLockForKey('records');
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.Legacy.records", {trace:false});
+ deferredResult.acquireLock(deferredLock);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._records == null) {
+ innerDeferredResult = new Clipperz.Async.Deferred("User.Header.Legacy.records <inner deferred>", {trace:false});
+ innerDeferredResult.collectResults({
+ 'header': [
+// MochiKit.Base.method(this, 'getObjectDataStore'),
+// MochiKit.Base.methodcaller('values')
+ MochiKit.Base.method(this, 'values')
+ ],
+ 'recordsStats': [
+ MochiKit.Base.method(this, 'getRemoteData'),
+ MochiKit.Base.itemgetter('recordsStats')
+ ]
+ });
+ innerDeferredResult.addCallback(MochiKit.Base.bind(function (someObjectData) {
+ var reference;
+
+ this._records = {};
+// this._directLogins = {};
+
+ for (reference in someObjectData['header']['records']) {
+ var record;
+
+ record = new Clipperz.PM.DataModel.Record({
+ 'reference': reference,
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getRecordKey'),
+ 'retrieveRemoteDataFunction': this.retrieveRecordDetailFunction(),
+// 'encryptedDataKeypath': 'data',
+// 'encryptedVersionKeypath': 'version',
+
+ 'retrieveIndexDataFunction': MochiKit.Base.method(this, 'getRecordIndexData'),
+ 'updateIndexDataFunction': MochiKit.Base.method(this, 'updateRecordIndexData'),
+ 'updateDate': someObjectData['recordsStats'][reference]['updateDate'],
+
+ 'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
+ 'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
+ 'removeDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData')
+ });
+
+ this._records[reference] = record;
+ }
+
+ for (reference in someObjectData['header']['directLogins']) {
+ var directLogin;
+ var record;
+
+ record = this._records[someObjectData['header']['directLogins'][reference]['record']];
+ if (record != null) {
+ directLogin = new Clipperz.PM.DataModel.DirectLogin({
+ 'reference': reference,
+ 'record': record //,
+// 'retrieveIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
+// 'setIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
+// 'removeIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData')
+ });
+ } else {
+Clipperz.log("WARNING: DIRECT LOGIN without a matching RECORD!!");
+ }
+ }
+
+ return this._records;
+ }, this));
+ innerDeferredResult.callback();
+ } else {
+ innerDeferredResult = MochiKit.Async.succeed(this._records);
+ }
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(deferredLock);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.Header.OneTimePasswords.js b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.OneTimePasswords.js
new file mode 100644
index 0000000..e82da47
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.OneTimePasswords.js
@@ -0,0 +1,117 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.User.Header.OneTimePasswords depends on Clipperz.PM.DataModel.User!";
+}
+if (typeof(Clipperz.PM.DataModel.User.Header) == 'undefined') { Clipperz.PM.DataModel.User.Header = {}; }
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.DataModel.User.Header.OneTimePasswords = function(args) {
+ Clipperz.PM.DataModel.User.Header.OneTimePasswords.superclass.constructor.apply(this, arguments);
+
+ this._oneTimePasswords = null;
+
+ return this;
+}
+
+//-----------------------------------------------------------------------------
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.OneTimePasswords, Clipperz.PM.DataModel.EncryptedRemoteObject, {
+
+ 'toString': function() {
+ return "Clipperz.PM.DataModel.User.Header.OneTimePasswords";
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'packData': function (someData) { // ++
+ var result;
+
+ result = Clipperz.PM.DataModel.User.Header.OneTimePasswords.superclass.packData.apply(this, arguments);
+
+ return result;
+ },
+*/
+ //-------------------------------------------------------------------------
+/*
+ 'packRemoteData': function (someData) {
+ var result;
+
+ result = Clipperz.PM.DataModel.User.Header.OneTimePasswords.superclass.packRemoteData.apply(this, arguments);
+
+ return result;
+ },
+*/
+ //-------------------------------------------------------------------------
+/*
+ 'prepareRemoteDataWithKey': function (aKey) {
+ var result;
+
+ result = Clipperz.PM.DataModel.User.Header.OneTimePasswords.superclass.prepareRemoteDataWithKey.apply(this, arguments);
+
+ return result;
+ },
+*/
+ //=========================================================================
+
+ 'oneTimePasswords': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.OneTimePasswords.oneTimePasswords", {trace:false});
+ if (this._oneTimePasswords == null) {
+ deferredResult.addMethod(this, 'values')
+ deferredResult.addCallback(MochiKit.Base.bind(function (someData) {
+ var otpKey;
+
+ this._oneTimePasswords = {};
+
+ for (otpKey in someData) {
+ var otp;
+ var otpParameters;
+
+ otpParameters = Clipperz.Base.deepClone(someData[otpKey]);
+ otpParameters['reference'] = otpKey;
+
+ otp = new Clipperz.PM.DataModel.OneTimePassword(otpParameters);
+ this._oneTimePasswords[otpKey] = otp;
+ }
+
+ return this._oneTimePasswords;
+
+ }, this));
+ deferredResult.callback();
+ } else {
+ deferredResult = MochiKit.Async.succeed(this._oneTimePasswords);
+ }
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+//-----------------------------------------------------------------------------
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Preferences.js b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Preferences.js
new file mode 100644
index 0000000..f1f95e8
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.Preferences.js
@@ -0,0 +1,48 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.User.Header.Preferences depends on Clipperz.PM.DataModel.User!";
+}
+
+if (typeof(Clipperz.PM.DataModel.User.Header) == 'undefined') { Clipperz.PM.DataModel.User.Header = {}; }
+
+Clipperz.PM.DataModel.User.Header.Preferences = function(args) {
+ Clipperz.PM.DataModel.User.Header.Preferences.superclass.constructor.apply(this, arguments);
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.Preferences, Clipperz.PM.DataModel.EncryptedRemoteObject, {
+
+ 'toString': function() {
+ return "Clipperz.PM.DataModel.User.Header.Preferences";
+ },
+
+ //-------------------------------------------------------------------------
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.Header.RecordIndex.js b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.RecordIndex.js
new file mode 100644
index 0000000..5681f70
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.Header.RecordIndex.js
@@ -0,0 +1,685 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.User.Header.RecordIndex depends on Clipperz.PM.DataModel.User!";
+}
+
+if (typeof(Clipperz.PM.DataModel.User.Header) == 'undefined') { Clipperz.PM.DataModel.User.Header = {}; }
+
+Clipperz.PM.DataModel.User.Header.RecordIndex = function(args) {
+ Clipperz.PM.DataModel.User.Header.RecordIndex.superclass.constructor.apply(this, arguments);
+
+ this._recordsData = new Clipperz.PM.DataModel.EncryptedRemoteObject({
+ 'name': 'recordsData',
+ 'retrieveKeyFunction': args.retrieveKeyFunction,
+ 'remoteData': {
+ 'data': args.recordsData['data'],
+ 'version': args.encryptedDataVersion,
+ 'recordsStats': args.recordsStats
+ }//,
+// 'encryptedDataKeypath': 'data',
+// 'encryptedVersionKeypath': 'version'
+ });
+
+ this._directLoginsData = new Clipperz.PM.DataModel.EncryptedRemoteObject({
+ 'name': 'directLoginsData',
+ 'retrieveKeyFunction': args.retrieveKeyFunction,
+ 'remoteData': {
+ 'data': args.directLoginsData['data'],
+ 'version': args.encryptedDataVersion
+ }//,
+// 'encryptedDataKeypath': 'data',
+// 'encryptedVersionKeypath': 'version'
+ });
+
+ this._lock = new MochiKit.Async.DeferredLock();
+ this._transientState = null;
+
+ this._retrieveRecordDetailFunction = args.retrieveRecordDetailFunction || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._recordsIndex = args.recordsData['index'] || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._directLoginsIndex = args.directLoginsData['index'] || Clipperz.Base.exception.raise('MandatoryParameter');
+
+ this._records = null;
+
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.RecordIndex, Object, {
+
+ 'toString': function() {
+ return "Clipperz.PM.DataModel.User.Header.RecordIndex";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'retrieveRecordDetailFunction': function () {
+ return this._retrieveRecordDetailFunction;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'recordsIndex': function () {
+ return this._recordsIndex;
+ },
+
+ 'recordsData': function () {
+ return this._recordsData;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'directLoginsIndex': function () {
+ return this._directLoginsIndex;
+ },
+
+ 'directLoginsData': function () {
+ return this._directLoginsData;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'lock': function () {
+ return this._lock;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'transientState': function () {
+ if (this._transientState == null) {
+ this._transientState = new Clipperz.KeyValueObjectStore(/*{'name':'User.Header.RecordIndex.transientState [1]'}*/);
+ }
+
+ return this._transientState;
+ },
+
+ 'resetTransientState': function (isCommitting) {
+ if (this._transientState != null) {
+ this._transientState.removeAllData();
+ }
+
+ this._transientState = null;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getRecordKey': function (aRecordReference) {
+ return Clipperz.Async.callbacks("User.Header.RecordIndex.getRecordKey", [
+ MochiKit.Base.method(this, 'getRecordIndexData', aRecordReference),
+ MochiKit.Base.itemgetter('key')
+ ], {trace:false});
+ },
+
+ 'setRecordKey': function (aRecordReference, aValue) {
+ return this.updateRecordIndexData(aRecordReference, 'key', aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getRecordIndexData': function (aRecordReference) {
+ return this.recordsData().getValue(this.recordsIndex()[aRecordReference]);
+ },
+
+ //.........................................................................
+
+ 'updateRecordIndexData': function (aRecordReference, aKey, aValue) {
+ return this.recordsData().setValue(this.recordsIndex()[aRecordReference]+'.'+aKey, aValue);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getDirectLoginIndexData': function (aDirectLoginReference) {
+ return this.directLoginsData().getValue(this.directLoginsIndex()[aDirectLoginReference]);
+ },
+
+ 'setDirectLoginIndexData': function (aDirectLoginReference, aKey, aValue) {
+//if (MochiKit.Base.isUndefinedOrNull(this.directLoginsIndex()[aDirectLoginReference])) {
+// throw "PIPPO";
+//}
+ return this.directLoginsData().setValue(this.directLoginsIndex()[aDirectLoginReference] + '.' + aKey, aValue);
+ },
+
+ 'addDirectLoginIndexData': function (aDirectLoginReference) {
+ return this.directLoginsData().setValue(this.directLoginsIndex()[aDirectLoginReference], {});
+ },
+
+ 'removeDirectLoginIndexData': function (aDirectLoginReference) {
+ return this.directLoginsData().removeValue(this.directLoginsIndex()[aDirectLoginReference])
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'records': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.records", {trace:false});
+ deferredResult.acquireLock(this.lock());
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ var innerDeferredResult;
+
+ if (this._records == null) {
+ innerDeferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.records <inner deferred>", {trace:false});
+ innerDeferredResult.collectResults({
+ 'records': [
+// MochiKit.Base.method(this.recordsData(), 'getObjectDataStore'),
+// MochiKit.Base.methodcaller('values')
+ MochiKit.Base.method(this.recordsData(), 'values')
+ ],
+ 'recordsStats': [
+ MochiKit.Base.method(this.recordsData(), 'getRemoteData'),
+ MochiKit.Base.itemgetter('recordsStats')
+ ],
+ 'directLogins': [
+// MochiKit.Base.method(this.directLoginsData(), 'getObjectDataStore'),
+// MochiKit.Base.methodcaller('values')
+ MochiKit.Base.method(this.directLoginsData(), 'values')
+ ]
+ })
+ innerDeferredResult.addCallback(MochiKit.Base.bind(function (someData) {
+ var indexReference;
+ var recordsInvertedIndex;
+ var directLoginsInvertedIndex;
+
+ recordsInvertedIndex = Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex(this.recordsIndex());
+ directLoginsInvertedIndex = Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex(this.directLoginsIndex());
+
+ this._records = {};
+
+ for (indexReference in someData['records']) {
+ var record;
+ var reference;
+ var updateDate;
+
+ reference = recordsInvertedIndex[indexReference];
+
+ if (typeof(someData['recordsStats'][reference]) != 'undefined') {
+ updateDate = someData['recordsStats'][reference]['updateDate'];
+
+ record = new Clipperz.PM.DataModel.Record({
+ 'reference': reference,
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getRecordKey'),
+ 'retrieveRemoteDataFunction': this.retrieveRecordDetailFunction(),
+
+ 'retrieveIndexDataFunction': MochiKit.Base.method(this, 'getRecordIndexData'),
+ 'updateIndexDataFunction': MochiKit.Base.method(this, 'updateRecordIndexData'),
+ 'updateDate': updateDate,
+
+ 'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
+ 'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
+ 'removeDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData'),
+
+ 'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin')
+ });
+
+ this._records[reference] = record;
+ } else {
+Clipperz.log("SKIPPING record " + reference + " as there are no stas associated - " + Clipperz.Base.serializeJSON(someData['records'][reference]));
+ // # skip the record, as it seems it is not present in the DB
+ // updateDate = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
+ }
+ }
+
+ for (indexReference in someData['directLogins']) {
+// var directLogin;
+ var reference;
+ var record;
+
+ reference = directLoginsInvertedIndex[indexReference];
+ record = this._records[recordsInvertedIndex[someData['directLogins'][indexReference]['record']]];
+
+ if (record != null) {
+// directLogin = new Clipperz.PM.DataModel.DirectLogin({
+ new Clipperz.PM.DataModel.DirectLogin({
+ 'reference': reference,
+ 'record': record
+ });
+ } else {
+ Clipperz.logWarning("WARNING: DIRECT LOGIN without a matching RECORD!!");
+ }
+ }
+
+ return this._records;
+ }, this));
+ innerDeferredResult.callback();
+ } else {
+ innerDeferredResult = MochiKit.Async.succeed(this._records);
+ }
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(this.lock());
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'updateRecordIndexForNewRecord': function (aNewRecord) {
+ var newRecordIndex;
+ var recordReference;
+
+ recordReference = aNewRecord.reference();
+ newRecordIndex = (MochiKit.Base.listMax(MochiKit.Base.map(MochiKit.Base.partial(MochiKit.Base.operator.mul, 1), MochiKit.Base.values(this.recordsIndex()))) + 1) + '';
+ this.recordsIndex()[recordReference] = newRecordIndex;
+
+ this.transientState().setValue('newlyCreatedRecordsIndex' + '.' + recordReference, newRecordIndex);
+ this.transientState().setValue('newlyCreatedRecordsReferences' + '.' + recordReference, aNewRecord);
+ },
+
+ //.........................................................................
+
+ 'createNewRecord': function () {
+ var deferredResult;
+ var newRecord;
+
+ newRecord = new Clipperz.PM.DataModel.Record({
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getRecordKey'),
+ 'retrieveRemoteDataFunction': this.retrieveRecordDetailFunction(),
+
+ 'retrieveIndexDataFunction': MochiKit.Base.method(this, 'getRecordIndexData'),
+ 'updateIndexDataFunction': MochiKit.Base.method(this, 'updateRecordIndexData'),
+ 'updateDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date()),
+
+ 'retrieveDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'getDirectLoginIndexData'),
+ 'setDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'setDirectLoginIndexData'),
+ 'removeDirectLoginIndexDataFunction': MochiKit.Base.method(this, 'removeDirectLoginIndexData'),
+
+ 'createNewDirectLoginFunction': MochiKit.Base.method(this, 'createNewDirectLogin')
+ });
+
+ this.transientState().setValue('newRecordsReferences' + '.' + newRecord.reference(), newRecord);
+ this.updateRecordIndexForNewRecord(newRecord);
+
+ deferredResult = Clipperz.Async.callbacks("User.Header.RecordIndex.createNewRecord", [
+ MochiKit.Base.method(this, 'records'),
+ MochiKit.Base.partial(Clipperz.Async.setItemOnObject, newRecord.reference(), newRecord),
+ MochiKit.Base.method(this, 'setRecordKey', newRecord.reference(), Clipperz.PM.Crypto.randomKey()),
+ MochiKit.Base.method(newRecord, 'setLabel', ''),
+ MochiKit.Base.partial(MochiKit.Async.succeed, newRecord)
+ ], {trace:false});
+
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'deleteRecord': function (aRecord) {
+ var deferredResult;
+ var recordReference;
+
+ recordReference = aRecord.reference();
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.deleteRecord", {trace:false});
+
+ deferredResult.addMethod(aRecord, 'directLogins');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'removeDirectLogin'));
+
+ deferredResult.addMethod(this.recordsData(), 'removeValue', this.recordsIndex()[recordReference]);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ this.transientState().setValue('deleteRecordsIndex' + '.' + recordReference, this.recordsIndex()[recordReference]);
+ delete this.recordsIndex()[recordReference];
+ }, this));
+
+ deferredResult.addMethod(this, 'records');
+ deferredResult.addCallback(MochiKit.Base.itemgetter(recordReference));
+ deferredResult.addMethod(this.transientState(), 'setValue', 'deleteRecordsReferences' + '.' + recordReference);
+
+ deferredResult.addMethod(this, 'records');
+ deferredResult.addCallback(MochiKit.Base.bind(function (someRecords) {
+ delete someRecords[recordReference];
+ }, this));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'removeDirectLogin': function (aDirectLogin) {
+ this.directLoginsData().removeValue(this.directLoginsIndex()[aDirectLogin.reference()]);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'createNewDirectLogin': function (aRecord) {
+ var newDirectLogin;
+ var newDirectLoginIndexValue;
+
+ newDirectLogin = new Clipperz.PM.DataModel.DirectLogin({record:aRecord});
+ newDirectLoginIndexValue = MochiKit.Base.listMax(MochiKit.Base.map(function (aValue) { return aValue * 1; }, MochiKit.Base.values(this.directLoginsIndex()))) + 1;
+
+ this.transientState().setValue('newDirectLoginReferences' + '.' + newDirectLogin.reference(), newDirectLogin);
+
+ this.directLoginsIndex()[newDirectLogin.reference()] = newDirectLoginIndexValue;
+ this.directLoginsData().setValue(this.directLoginsIndex()[newDirectLogin.reference()], {'record': this.recordsIndex()[aRecord.reference()]});
+
+ return newDirectLogin;
+ },
+
+ //=========================================================================
+
+ 'deleteAllCleanTextData': function () {
+ return Clipperz.Async.callbacks("User.Header.RecordIndex.deleteAllCleanTextData", [
+// MochiKit.Base.method(this, 'records'),
+// MochiKit.Base.values,
+// MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
+
+ MochiKit.Base.method(this, 'recordsData'),
+ MochiKit.Base.methodcaller('deleteAllCleanTextData'),
+ MochiKit.Base.method(this, 'directLoginsData'),
+ MochiKit.Base.methodcaller('deleteAllCleanTextData')
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasAnyCleanTextData': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred({trace:false});
+ deferredResult.collectResults({
+ 'recordsData': [
+ MochiKit.Base.method(this, 'recordsData'),
+ MochiKit.Base.methodcaller('hasAnyCleanTextData')
+ ],
+ 'directLoginsData': [
+ MochiKit.Base.method(this, 'directLoginsData'),
+ MochiKit.Base.methodcaller('hasAnyCleanTextData')
+ ],
+// 'records': [
+// MochiKit.Base.method(this, 'records'),
+// MochiKit.Base.values,
+// MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
+// Clipperz.Async.collectAll
+// ]
+ });
+
+// deferredResult.addCallback(MochiKit.Base.values);
+// deferredResult.addCallback(MochiKit.Base.flattenArguments);
+// deferredResult.addCallback(function(someValues) {
+// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+// });
+ deferredResult.addCallback(Clipperz.Async.or);
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasPendingChanges': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.hasPendingChanges", {trace:false});
+ deferredResult.collectResults({
+ 'recordsData': [
+ MochiKit.Base.method(this, 'recordsData'),
+ MochiKit.Base.methodcaller('hasPendingChanges')
+ ],
+ 'directLoginsData': [
+ MochiKit.Base.method(this, 'directLoginsData'),
+ MochiKit.Base.methodcaller('hasPendingChanges')
+ ]
+ });
+ deferredResult.addCallback(Clipperz.Async.or);
+// deferredResult.addCallback(MochiKit.Base.values);
+// deferredResult.addCallback(MochiKit.Base.flattenArguments);
+// deferredResult.addCallback(function(someValues) {
+// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
+// });
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'commitTransientState': function () {
+ var deferredResult;
+
+ deferredResut = Clipperz.Async.callbacks("User.Header.RecordIndex.commitTransientState", [
+ MochiKit.Base.method(this, 'recordsData'),
+ MochiKit.Base.methodcaller('commitTransientState'),
+
+ MochiKit.Base.method(this, 'directLoginsData'),
+ MochiKit.Base.methodcaller('commitTransientState'),
+
+ MochiKit.Base.method(this, 'resetTransientState', true)
+ ], {trace:false});
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'revertChanges': function () {
+ return Clipperz.Async.callbacks("User.Header.RecordIndex.revertChanges", [
+ MochiKit.Base.method(this, 'recordsData'),
+ MochiKit.Base.methodcaller('revertChanges'),
+
+// MochiKit.Base.method(this, 'directLoginsData'),
+// MochiKit.Base.methodcaller('revertChanges'),
+
+ MochiKit.Base.method(this, 'records'),
+ MochiKit.Base.bind(function (someRecords) {
+ var recordReference;
+
+ for (recordReference in this.transientState().getValue('deleteRecordsReferences')) {
+ this.recordsIndex()[recordReference] = this.transientState().getValue('deleteRecordsIndex' + '.' + recordReference);
+ someRecords[recordReference] = this.transientState().getValue('deleteRecordsReferences' + '.' + recordReference);
+ }
+
+ for (recordReference in this.transientState().getValue('newRecordsReferences')) {
+ delete this.recordsIndex()[recordReference];
+ delete someRecords[recordReference];
+ }
+ }, this),
+
+// MochiKit.Base.method(this, 'directLogins'),
+ MochiKit.Base.bind(function () {
+ var directLoginReference;
+
+// this.transientState().setValue('newDirectLoginReferences' + '.' + newDirectLogin.reference(), newDirectLogin);
+//
+// this.directLoginsIndex()[newDirectLogin.reference()] = newDirectLoginIndexValue;
+// this.directLoginsData().setValue(this.directLoginsIndex()[newDirectLogin.reference()], {'record': this.recordsIndex()[aRecord.reference()]});
+
+
+// for (directLoginReference in this.transientState().getValue('deleteDirectLoginReferences')) {
+// someDirectLogins[directLoginReference] = this.transientState().getValue('deleteDirectLoginReferences' + '.' + recordReference);
+// }
+
+ for (directLoginReference in this.transientState().getValue('newDirectLoginReferences')) {
+// this.directLoginsData().removeValue(this.directLoginsIndex()[directLoginReference]);
+ delete this.directLoginsIndex()[directLoginReference];
+ }
+ }, this),
+
+ MochiKit.Base.method(this, 'directLoginsData'),
+ MochiKit.Base.methodcaller('revertChanges'),
+
+ MochiKit.Base.method(this, 'resetTransientState', false)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'prepareRemoteDataWithKey': function (aKey) {
+// "records": {
+// "index": {
+// "eeda70e0392261967bda71c3764da78989c45bbd2bb7be6b941b90f81d9b81b5": "0",
+// "13a5e52976337ab210903cd04872588e1b21fb72bc183e91aa25c494b8138551": "1",
+// ...
+// "465a067a0bd2b470fa834de5397e38494de0c7707938262fae3427932e219744": "18",
+// "4fd1dc2ca860b7fb47cef10a84edb3270da05510b0a30a6b0b083898712d4b9e": "19"
+// },
+// "data": "n+AzGEEQXaSRSY4d ... BDypotrXgPo94uHfoXvGFzwCn8w="
+// },
+// "directLogins": {
+// "index": {
+// "61e87fdc4f1d9112e3b30c1f6812d095dcdb24f014c83319091eb6c9899ec348":"0",
+// "989593d4c48929f0c8f1581aa96969c622807e99619ed4732026e967530a68ad":"1",
+// ...
+// "cb9ae0bba1957075ccdbfd3b3481704d62087687a2ac7c411a4f07d444bde0f7":"17",
+// "7e1d069b7fa57c03bd7bf48807520feb953157834503aaff8c9d493f37dea69d":"18"
+// },
+// "data":"5YG9KKU/OZ5guUgFlms6k1 ... ZG/5Fn0uN+LoAsNfHm+EE62x"
+// },
+
+ var deferredResult;
+ var result;
+
+ result = {};
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.prepareRemoteDataWithKey", {trace:false});
+ deferredResult.collectResults({
+ 'index': MochiKit.Base.partial(MochiKit.Async.succeed, this.recordsIndex()),
+ 'data': [
+ MochiKit.Base.method(this.recordsData(), 'prepareRemoteDataWithKey', aKey),
+ MochiKit.Base.itemgetter('data')
+ ]
+ });
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'records');
+
+ deferredResult.collectResults({
+ 'index': MochiKit.Base.partial(MochiKit.Async.succeed, this.directLoginsIndex()),
+ 'data': [
+ MochiKit.Base.method(this.directLoginsData(), 'prepareRemoteDataWithKey', aKey),
+ MochiKit.Base.itemgetter('data')
+ ]
+ });
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'directLogins');
+
+ deferredResult.addCallback(MochiKit.Async.succeed, result);
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'updateRecordKeyAndPrepareRemoteData': function (aRecord) {
+ var newRecordKey;
+ var deferredResult;
+
+ newRecordKey = Clipperz.PM.Crypto.randomKey();
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.updateRecordKeyAndPrepareRemoteData", {trace:false});
+ deferredResult.addCallback(MochiKit.Base.method(aRecord, 'prepareRemoteDataWithKey', newRecordKey));
+ deferredResult.addCallbackPass(MochiKit.Base.method(this, 'setRecordKey', aRecord.reference(), newRecordKey));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //.........................................................................
+
+ 'removeNewRecordWithNoChanges': function (aRecord) {
+ var deferredResult;
+ var recordReference;
+
+ recordReference = aRecord.reference();
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.removeNewRecordWithNoChanges", {trace:false});
+
+ deferredResult.addMethod(this.recordsData(), 'removeValue', this.recordsIndex()[recordReference]);
+ deferredResult.addCallback(MochiKit.Base.bind(function () {
+ delete this.recordsIndex()[recordReference];
+ }, this));
+
+ deferredResult.addMethod(this, 'records');
+ deferredResult.addCallback(MochiKit.Base.bind(function (someRecords) {
+ delete someRecords[recordReference];
+ }, this));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //.........................................................................
+
+ 'prepareRemoteDataForChangedRecords': function () {
+ var deferredResult;
+ var result;
+
+ result = {};
+
+ deferredResult = new Clipperz.Async.Deferred("User.Header.RecordIndex.prepareRemoteDataForChangedRecords", {trace:false});
+
+ deferredResult.addMethod(this, 'records');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(Clipperz.Async.deferredFilter, MochiKit.Base.methodcaller('isBrandNewWithNoPendingChanges'));
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'removeNewRecordWithNoChanges'));
+
+ deferredResult.addMethod(this, 'records');
+ deferredResult.addCallback(MochiKit.Base.values);
+ deferredResult.addCallback(Clipperz.Async.deferredFilter, MochiKit.Base.methodcaller('hasPendingChanges'));
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'updateRecordKeyAndPrepareRemoteData'));
+ deferredResult.addCallback(Clipperz.Async.collectAll);
+
+ deferredResult.addCallback(Clipperz.Async.deferredIf("updated records != null", [
+ MochiKit.Base.operator.identity
+ ], [
+ MochiKit.Base.partial(MochiKit.Async.succeed, [])
+ ]));
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'updated');
+
+ deferredResult.addMethod(this.transientState(), 'getValue', 'deleteRecordsReferences');
+ deferredResult.addCallback(MochiKit.Base.keys);
+ deferredResult.addCallback(Clipperz.Async.deferredIf("deleted records != null", [
+ MochiKit.Base.operator.identity
+ ], [
+ MochiKit.Base.partial(MochiKit.Async.succeed, [])
+ ]));
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'deleted');
+
+ deferredResult.addCallback(MochiKit.Async.succeed, result);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
+
+
+
+Clipperz.PM.DataModel.User.Header.RecordIndex.invertIndex = function (anIndex) {
+ var result;
+ var key;
+
+ result = {};
+
+ for (key in anIndex) {
+ result[anIndex[key]] = key;
+ }
+
+ return result;
+}; \ No newline at end of file
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.Subscription.js b/frontend/delta/js/Clipperz/PM/DataModel/User.Subscription.js
new file mode 100644
index 0000000..341e9f3
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.Subscription.js
@@ -0,0 +1,53 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.DataModel.User.Subscription depends on Clipperz.PM.DataModel.User!";
+}
+
+Clipperz.PM.DataModel.User.Subscription = function(args) {
+ this._attributes = args;
+ return this;
+}
+
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User.Subscription, Object, {
+
+ 'features': function () {
+ return this._attributes['features'];
+ },
+
+ 'type': function () {
+ return this._attributes['type'];
+ },
+
+ 'validity': function () {
+ return {
+ 'from': this._attributes['fromDate'],
+ 'to': this._attributes['toDate']
+ };
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/User.js b/frontend/delta/js/Clipperz/PM/DataModel/User.js
new file mode 100644
index 0000000..1d90800
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/User.js
@@ -0,0 +1,827 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
+
+
+//#############################################################################
+
+Clipperz.PM.DataModel.User = function (args) {
+ args = args || {};
+
+ Clipperz.PM.DataModel.User.superclass.constructor.apply(this, arguments);
+
+ this._username = args.username || null;
+ this._getPassphraseFunction = args.getPassphraseFunction || null;
+
+ this._data = null;
+
+ this._connection = null;
+ this._connectionVersion = 'current';
+
+ this._subscription = null;
+ this._serverData = null;
+// this._serverLockValue = null;
+ this._transientState = null;
+
+ this._deferredLocks = {
+ 'passphrase': new MochiKit.Async.DeferredLock(),
+ 'serverData': new MochiKit.Async.DeferredLock(),
+// 'recordsIndex': new MochiKit.Async.DeferredLock(),
+// 'directLoginsIndex': new MochiKit.Async.DeferredLock()
+// 'preferences': new MochiKit.Async.DeferredLock()
+// 'oneTimePasswords': new MochiKit.Async.DeferredLock()
+ '__syntaxFix__': 'syntax fix'
+ };
+
+ return this;
+}
+
+Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
+
+ 'toString': function () {
+ return "Clipperz.PM.DataModel.User - " + this.username();
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'username': function () {
+ return this._username;
+ },
+
+ 'setUsername': function (aValue) {
+ this._username = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+// this.setSubscription(new Clipperz.PM.DataModel.User.Subscription(someServerData['subscription']));
+ 'subscription': function () {
+ return this._subscription;
+ },
+
+ 'setSubscription': function (aValue) {
+ this._subscription = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'displayName': function() {
+ return "" + this.username() + "";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'data': function () {
+ if (this._data == null) {
+ this._data = new Clipperz.KeyValueObjectStore(/*{'name':'User.data [1]'}*/);
+ };
+
+ return this._data;
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ 'serverLockValue': function () {
+ return this._serverLockValue;
+ },
+
+ 'setServerLockValue': function (aValue) {
+ this._serverLockValue = aValue;
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'transientState': function () {
+ if (this._transientState == null) {
+ this._transientState = {}
+ }
+
+ return this._transientState;
+ },
+
+ 'resetTransientState': function (isCommitting) {
+ this._transientState = null;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'deferredLockForSection': function(aSectionName) {
+ return this._deferredLocks[aSectionName];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getPassphrase': function() {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.getPassphrase", {trace:false});
+ deferredResult.acquireLock(this.deferredLockForSection('passphrase'));
+ deferredResult.addMethod(this.data(), 'deferredGetOrSet', 'passphrase', this.getPassphraseFunction());
+ deferredResult.releaseLock(this.deferredLockForSection('passphrase'));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ 'getPassphraseFunction': function () {
+ return this._getPassphraseFunction;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getCredentials': function () {
+ return Clipperz.Async.collectResults("User; get username and passphrase", {
+ 'username': MochiKit.Base.method(this, 'username'),
+ 'password': MochiKit.Base.method(this, 'getPassphrase')
+ }, {trace:false})();
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'changePassphrase': function (aNewValue) {
+ return this.updateCredentials(this.username(), aNewValue);
+ },
+
+ //.........................................................................
+
+ 'updateCredentials': function (aUsername, aPassphrase) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.updateCredentials", {trace:false});
+// deferredResult.addMethod(this, 'getPassphrase');
+// deferredResult.setValue('currentPassphrase');
+ deferredResult.addMethod(this.connection(), 'ping');
+ deferredResult.addMethod(this, 'setUsername', aUsername)
+ deferredResult.acquireLock(this.deferredLockForSection('passphrase'));
+ deferredResult.addMethod(this.data(), 'deferredGetOrSet', 'passphrase', aPassphrase);
+ deferredResult.releaseLock(this.deferredLockForSection('passphrase'));
+// deferredResult.getValue('currentPassphrase');
+ deferredResult.addMethod(this, 'prepareRemoteDataWithKey', aPassphrase);
+ deferredResult.addMethod(this.connection(), 'updateCredentials', aUsername, aPassphrase);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'initialSetupWithNoData': function () {
+ this._serverData = {
+ 'version': '0.1',
+ 'statistics': "",
+ 'header': {
+ 'data': null,
+ 'version': Clipperz.PM.Crypto.encryptingFunctions.currentVersion,
+
+ 'recordsIndex': new Clipperz.PM.DataModel.User.Header.RecordIndex({
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
+ 'recordsData': {'data':null, 'index':{}},
+ 'recordsStats': null,
+ 'directLoginsData': {'data':null, 'index':{}},
+ 'encryptedDataVersion': Clipperz.PM.Crypto.encryptingFunctions.currentVersion,
+ 'retrieveRecordDetailFunction': MochiKit.Base.method(this, 'getRecordDetail')
+ }),
+ 'preferences': new Clipperz.PM.DataModel.User.Header.Preferences({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
+ }),
+ 'oneTimePasswords': new Clipperz.PM.DataModel.User.Header.OneTimePasswords({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
+ })
+ }
+ };
+
+// this._serverLockValue = Clipperz.PM.Crypto.randomKey();
+ },
+
+ //.........................................................................
+
+ 'registerAsNewAccount': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.registerAsNewAccount", {trace:false});
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3});
+ deferredResult.addMethod(this, 'initialSetupWithNoData')
+ deferredResult.addMethod(this, 'getPassphrase');
+ deferredResult.addMethod(this, 'prepareRemoteDataWithKey');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+ deferredResult.addMethod(this.connection(), 'register');
+// deferredResult.addCallback(MochiKit.Base.itemgetter('lock'));
+// deferredResult.addMethod(this, 'setServerLockValue');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userSuccessfullyRegistered');
+
+// deferredResult.addErrback (MochiKit.Base.method(this, 'handleRegistrationFailure'));
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'login': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.login", {trace:false});
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':3});
+ deferredResult.addMethod(this, 'getPassphrase');
+ deferredResult.addCallback(Clipperz.PM.DataModel.OneTimePassword.isValidOneTimePasswordValue);
+ deferredResult.addCallback(Clipperz.Async.deferredIf("Is the passphrase an OTP", [
+// MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'updateProgress', {'extraSteps':1}),
+ MochiKit.Base.method(this, 'getCredentials'),
+ MochiKit.Base.method(this.connection(), 'redeemOneTimePassword'),
+ MochiKit.Base.method(this.data(), 'setValue', 'passphrase')
+ ], []));
+ deferredResult.addErrback(MochiKit.Base.method(this, 'getPassphrase'));
+ deferredResult.addMethod(this.connection(), 'login', false);
+ deferredResult.addMethod(this, 'setupConnectionInfo');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userSuccessfullyLoggedIn');
+ deferredResult.addErrback (MochiKit.Base.method(this, 'handleConnectionFallback'));
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //.........................................................................
+
+ 'handleConnectionFallback': function(aValue) {
+ var result;
+
+//console.log("USER - handleConnectionFallback", aValue, aValue['isPermanent']);
+ if (aValue instanceof MochiKit.Async.CancelledError) {
+ result = aValue;
+ } else if ((aValue['isPermanent'] === true) || (Clipperz.PM.Connection.communicationProtocol.fallbackVersions[this.connectionVersion()] == null)) {
+ result = Clipperz.Async.callbacks("User.handleConnectionFallback - failed", [
+ MochiKit.Base.method(this.data(), 'removeValue', 'passphrase'),
+ MochiKit.Base.method(this, 'setConnectionVersion', 'current'),
+// MochiKit.Base.partial(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userLoginFailed'),
+// MochiKit.Base.partial(MochiKit.Async.fail, Clipperz.PM.DataModel.User.exception.LoginFailed)
+ MochiKit.Base.partial(MochiKit.Async.fail, aValue)
+ ], {trace:false});
+ } else {
+ this.setConnectionVersion(Clipperz.PM.Connection.communicationProtocol.fallbackVersions[this.connectionVersion()]);
+ result = new Clipperz.Async.Deferred("User.handleConnectionFallback - retry");
+ result.addMethod(this, 'login');
+ result.callback();
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setupConnectionInfo': function (aValue) {
+// this.setLoginInfo(aValue['loginInfo']);
+ this.setSubscription(new Clipperz.PM.DataModel.User.Subscription(aValue['subscription']));
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'lock': function () {
+ return Clipperz.Async.callbacks("User.lock", [
+ MochiKit.Base.method(this, 'deleteAllCleanTextData')
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'logout': function () {
+ return Clipperz.Async.callbacks("User.logout", [
+ MochiKit.Base.method(this, 'deleteAllCleanTextData'),
+ MochiKit.Base.method(this.connection(), 'logout')
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'headerFormatVersion': function(anHeader) {
+ var result;
+
+ if (anHeader.charAt(0) == '{') {
+ var headerData;
+
+ headerData = Clipperz.Base.evalJSON(anHeader);
+ result = headerData['version'];
+ } else {
+ result = 'LEGACY';
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'unpackServerData': function (someServerData) {
+ var unpackedData;
+ var headerVersion;
+
+ var recordsIndex;
+ var preferences;
+ var oneTimePasswords;
+
+// this.setServerLockValue(someServerData['lock']);
+
+ headerVersion = this.headerFormatVersion(someServerData['header']);
+ switch (headerVersion) {
+ case 'LEGACY':
+ var legacyHeader;
+
+ legacyHeader = new Clipperz.PM.DataModel.User.Header.Legacy({
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
+ 'remoteData': {
+ 'data': someServerData['header'],
+ 'version': someServerData['version'],
+ 'recordsStats': someServerData['recordsStats']
+ },
+// 'encryptedDataKeypath': 'data',
+// 'encryptedVersionKeypath': 'version',
+ 'retrieveRecordDetailFunction': MochiKit.Base.method(this, 'getRecordDetail')
+ });
+
+ recordsIndex = legacyHeader;
+ preferences = legacyHeader;
+ oneTimePasswords = legacyHeader;
+ break;
+ case '0.1':
+ var headerData;
+
+ headerData = Clipperz.Base.evalJSON(someServerData['header']);
+
+ recordsIndex = new Clipperz.PM.DataModel.User.Header.RecordIndex({
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
+ 'recordsData': headerData['records'],
+ 'recordsStats': someServerData['recordsStats'],
+ 'directLoginsData': headerData['directLogins'],
+ 'encryptedDataVersion': someServerData['version'],
+ 'retrieveRecordDetailFunction': MochiKit.Base.method(this, 'getRecordDetail')
+ });
+
+ // Still missing a test case that actually fais with the old version of the code, where the check for undefined was missing
+ if (typeof(headerData['preferences']) != 'undefined') {
+ preferences = new Clipperz.PM.DataModel.User.Header.Preferences({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
+ 'remoteData': {
+ 'data': headerData['preferences']['data'],
+ 'version': someServerData['version']
+ }
+ });
+ } else {
+ preferences = new Clipperz.PM.DataModel.User.Header.Preferences({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
+ });
+ }
+
+ if (typeof(headerData['oneTimePasswords']) != 'undefined') {
+ oneTimePasswords = new Clipperz.PM.DataModel.User.Header.OneTimePasswords({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
+ 'remoteData': {
+ 'data': headerData['oneTimePasswords']['data'],
+ 'version': someServerData['version']
+ }
+ });
+ } else {
+ oneTimePasswords = new Clipperz.PM.DataModel.User.Header.OneTimePasswords({
+ 'name': 'preferences',
+ 'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
+ });
+ }
+
+ break;
+ }
+
+ unpackedData = {
+ 'version': someServerData['version'],
+ 'statistics': someServerData['statistics'],
+ 'header': {
+ 'data': someServerData['header'],
+ 'version': headerVersion,
+
+ 'recordsIndex': recordsIndex,
+ 'preferences': preferences,
+ 'oneTimePasswords': oneTimePasswords
+ }
+ };
+
+ this._serverData = unpackedData;
+
+ return this._serverData;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getServerData': function() {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.getServerData", {trace:false});
+ deferredResult.acquireLock(this.deferredLockForSection('serverData'));
+ deferredResult.addCallback(MochiKit.Base.bind(function(aResult) {
+ var innerDeferredResult;
+
+ innerDeferredResult = new Clipperz.Async.Deferred("User.getUserDetails.innerDeferred", {trace:false});
+ if (this._serverData == null) {
+ innerDeferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'loadingUserDetails');
+ innerDeferredResult.addMethod(this.connection(), 'message', 'getUserDetails');
+ innerDeferredResult.addMethod(this, 'unpackServerData');
+ innerDeferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'loadedUserDetails');
+ }
+
+ innerDeferredResult.addCallback(MochiKit.Base.bind(function () {
+ return this._serverData;
+ },this));
+ innerDeferredResult.callback();
+
+ return innerDeferredResult;
+ }, this));
+ deferredResult.releaseLock(this.deferredLockForSection('serverData'));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'connectionVersion': function() {
+ return this._connectionVersion;
+ },
+
+ 'setConnectionVersion': function(aValue) {
+ if (this._connectionVersion != aValue) {
+ this.resetConnection();
+ }
+ this._connectionVersion = aValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'connection': function() {
+ if ((this._connection == null) && (this.connectionVersion() != null) ){
+ this._connection = new Clipperz.PM.Connection.communicationProtocol.versions[this.connectionVersion()]({
+ getCredentialsFunction: MochiKit.Base.method(this, 'getCredentials')
+ });
+ }
+
+ return this._connection;
+ },
+
+ 'resetConnection': function(aValue) {
+ if (this._connection != null) {
+ this._connection.reset();
+ }
+
+ this._connection = null;
+ },
+
+ //=========================================================================
+
+ 'getHeaderIndex': function (aKey) {
+ return Clipperz.Async.callbacks("User.getHeaderIndex", [
+ MochiKit.Base.method(this, 'getServerData'),
+ MochiKit.Base.itemgetter('header'),
+ MochiKit.Base.itemgetter(aKey)
+ ], {trace:false})
+ },
+
+ //=========================================================================
+
+ 'getRecords': function () {
+ return Clipperz.Async.callbacks("User.getRecords", [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
+ MochiKit.Base.methodcaller('records'),
+ MochiKit.Base.values
+ ], {trace:false});
+ },
+
+ 'recordWithLabel': function (aLabel) {
+ return Clipperz.Async.callbacks("User.recordWithLabel", [
+ MochiKit.Base.method(this, 'getRecords'),
+ MochiKit.Base.partial(Clipperz.Async.deferredFilter, function (aRecord) {
+ return Clipperz.Async.callbacks("User.recordWithLabel - check record label", [
+ MochiKit.Base.methodcaller('label'),
+ MochiKit.Base.partial(MochiKit.Base.operator.eq, aLabel)
+ ], {trace:false}, aRecord);
+ }),
+ function (someFilteredResults) {
+ var result;
+
+ switch (someFilteredResults.length) {
+ case 0:
+ result = null;
+ break;
+ case 1:
+ result = someFilteredResults[0];
+ break;
+ default:
+ WTF = TODO;
+ break;
+ }
+
+ return result;
+ }
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getRecord': function (aRecordReference) {
+ return Clipperz.Async.callbacks("User.getRecord", [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
+ MochiKit.Base.methodcaller('records'),
+ MochiKit.Base.itemgetter(aRecordReference),
+
+ Clipperz.Async.deferredIf("record != null", [
+ MochiKit.Base.operator.identity
+ ], [
+ function () { throw "Record does not exists"}
+ ])
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getRecordDetail': function (aRecordReference) {
+ return this.connection().message('getRecordDetail', {reference: aRecordReference});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'deleteRecord': function (aRecord) {
+ return Clipperz.Async.callbacks("User.deleteRecord", [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
+ MochiKit.Base.methodcaller('deleteRecord', aRecord)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'createNewRecord': function () {
+ return Clipperz.Async.callbacks("User.createNewRecord", [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
+ MochiKit.Base.methodcaller('createNewRecord')
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'getDirectLogins': function() {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.getDirectLogins", {trace:false});
+ deferredResult.addMethod(this, 'getRecords');
+ deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.compose(MochiKit.Base.values, MochiKit.Base.methodcaller('directLogins')));
+ deferredResult.addCallback(MochiKit.Base.flattenArray);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'getOneTimePasswords': function () {
+ return Clipperz.Async.callbacks("User.getOneTimePasswords", [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'oneTimePasswords'),
+ MochiKit.Base.methodcaller('oneTimePasswords'),
+ MochiKit.Base.values
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'invokeMethodNamedOnHeader': function (aMethodName, aValue) {
+ return Clipperz.Async.collectResults("User.invokeMethodNamedOnHeader [" + aMethodName + "]", {
+ 'recordIndex': [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'recordsIndex'),
+ MochiKit.Base.methodcaller(aMethodName, aValue)
+ ],
+ 'preferences': [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'preferences'),
+ MochiKit.Base.methodcaller(aMethodName, aValue)
+ ],
+ 'oneTimePasswords': [
+ MochiKit.Base.method(this, 'getHeaderIndex', 'oneTimePasswords'),
+ MochiKit.Base.methodcaller(aMethodName, aValue)
+ ]//,
+// 'statistics': [
+// MochiKit.Base.method(this, 'getStatistics'),
+// MochiKit.Base.methodcaller(aMethodName, aValue)
+// ]
+ }, {trace:false})();
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'invokeMethodNamedOnRecords': function (aMethodName, aValue) {
+ return Clipperz.Async.callbacks("User.invokeMethodNamedOnRecords[" + aMethodName + "]", [
+ MochiKit.Base.method(this, 'getRecords'),
+ MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller(aMethodName, aValue)),
+ Clipperz.Async.collectAll
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'hasPendingChanges': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.hasPendingChanges", {trace:false});
+ deferredResult.collectResults({
+ 'header': [
+ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'hasPendingChanges'),
+ MochiKit.Base.values
+ ],
+ 'records': MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'hasPendingChanges')
+ });
+ deferredResult.addCallback(Clipperz.Async.or);
+ deferredResult.callback();
+// recordsIndex = legacyHeader;
+// preferences = legacyHeader;
+// oneTimePasswords = legacyHeader;
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'commitTransientState': function () {
+ return Clipperz.Async.callbacks("User.commitTransientState", [
+ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'commitTransientState'),
+ MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'commitTransientState'),
+
+ MochiKit.Base.method(this, 'transientState'),
+// MochiKit.Base.itemgetter('lock'),
+// MochiKit.Base.method(this, 'setServerLockValue'),
+ MochiKit.Base.method(this, 'resetTransientState', true)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'revertChanges': function () {
+ return Clipperz.Async.callbacks("User.revertChanges", [
+ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'revertChanges'),
+ MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'revertChanges'),
+ MochiKit.Base.method(this, 'resetTransientState', false)
+ ], {trace:false});
+ },
+
+ //=========================================================================
+
+ 'deleteAllCleanTextData': function () {
+ return Clipperz.Async.callbacks("User.deleteAllCleanTextData", [
+ MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'deleteAllCleanTextData'),
+ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'deleteAllCleanTextData'),
+
+ MochiKit.Base.method(this.data(), 'removeAllData'),
+ MochiKit.Base.method(this, 'resetTransientState', false)
+ ], {trace:false});
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'hasAnyCleanTextData': function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("User.hasAnyCleanTextData", {trace:false});
+ deferredResult.collectResults({
+ 'header': [
+ MochiKit.Base.method(this, 'invokeMethodNamedOnHeader', 'hasAnyCleanTextData'),
+ MochiKit.Base.values
+ ],
+ 'records': MochiKit.Base.method(this, 'invokeMethodNamedOnRecords', 'hasAnyCleanTextData'),
+ 'data': MochiKit.Base.bind(function () {
+ return MochiKit.Async.succeed(! this.data().isEmpty());
+ }, this),
+ 'transientState': MochiKit.Base.bind(function () {
+ return MochiKit.Async.succeed(MochiKit.Base.keys(this.transientState()).length != 0);
+ }, this)
+ });
+ deferredResult.addCallback(Clipperz.Async.or);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'prepareRemoteDataWithKey': function (aKey /*, aCurrentKey*/) {
+ var deferredResult;
+ var result;
+
+ result = {};
+ deferredResult = new Clipperz.Async.Deferred("User.prepareRemoteDataWithKey", {trace:false});
+ deferredResult.addMethod(this, 'invokeMethodNamedOnHeader', 'prepareRemoteDataWithKey', aKey /*, aCurrentKey*/);
+ deferredResult.addCallback(MochiKit.Base.bind(function (aResult, someHeaderPackedData) {
+ var header;
+
+ header = {};
+ header['records'] = someHeaderPackedData['recordIndex']['records'];
+ header['directLogins'] = someHeaderPackedData['recordIndex']['directLogins'];
+ header['preferences'] = {'data': someHeaderPackedData['preferences']['data']};
+ header['oneTimePasswords'] = {'data': someHeaderPackedData['oneTimePasswords']['data']};
+ header['version'] = '0.1';
+
+ aResult['header'] = Clipperz.Base.serializeJSON(header);
+ aResult['statistics'] = this._serverData['statistics']; // "someHeaderPackedData['statistics']['data']";
+
+ return aResult;
+ }, this), result);
+ deferredResult.addCallback(Clipperz.Async.setItem, result, 'version', Clipperz.PM.Crypto.encryptingFunctions.currentVersion);
+// deferredResult.addCallback(Clipperz.Async.setItem, result, 'lock', this.serverLockValue());
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'saveChanges': function () {
+ var deferredResult;
+ var messageParameters;
+
+ messageParameters = {};
+
+ deferredResult = new Clipperz.Async.Deferred("User.saveChangaes", {trace:false});
+
+ deferredResult.addMethod(this, 'getHeaderIndex', 'recordsIndex');
+ deferredResult.addCallback(MochiKit.Base.methodcaller('prepareRemoteDataForChangedRecords'));
+ deferredResult.addCallback(Clipperz.Async.setItem, messageParameters, 'records');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+
+ deferredResult.addMethod(this, 'getPassphrase');
+ deferredResult.addMethod(this, 'prepareRemoteDataWithKey');
+ deferredResult.addCallback(Clipperz.Async.setItem, messageParameters, 'user');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+
+ deferredResult.addCallback(MochiKit.Async.succeed, messageParameters);
+ deferredResult.addMethod(this.connection(), 'message', 'saveChanges');
+ deferredResult.addCallback(MochiKit.Base.update, this.transientState())
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+
+ deferredResult.addMethod(this, 'commitTransientState');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'advanceProgress');
+// deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'userDataSuccessfullySaved');
+
+ deferredResult.addErrbackPass(MochiKit.Base.method(this, 'revertChanges'));
+// deferredResult.addErrbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'failureWhileSavingUserData');
+
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.DataModel.User.registerNewAccount = function (anUsername, aPassphraseFunction) {
+ var deferredResult;
+ var user;
+
+ user = new Clipperz.PM.DataModel.User({'username':anUsername, 'getPassphraseFunction':aPassphraseFunction});
+
+ deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.User.registerNewAccount", {trace:false});
+ deferredResult.addMethod(user, 'registerAsNewAccount');
+ deferredResult.addMethod(user, 'login');
+ deferredResult.addCallback(MochiKit.Async.succeed, user);
+ deferredResult.callback();
+
+ return deferredResult;
+}
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.DataModel.User.exception = {
+ LoginFailed: new MochiKit.Base.NamedError("Clipperz.PM.DataModel.User.exception.LoginFailed"),
+ CredentialUpgradeFailed: new MochiKit.Base.NamedError("Clipperz.PM.DataModel.User.exception.CredentialUpgradeFailed")
+};
+
+//-----------------------------------------------------------------------------
diff --git a/frontend/delta/js/Clipperz/PM/Date.js b/frontend/delta/js/Clipperz/PM/Date.js
new file mode 100644
index 0000000..a62857e
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Date.js
@@ -0,0 +1,196 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.Date) == 'undefined') { Clipperz.PM.Date = {}; }
+
+Clipperz.PM.Date.VERSION = "0.1";
+Clipperz.PM.Date.NAME = "Clipperz.PM.Date";
+
+MochiKit.Base.update(Clipperz.PM.Date, {
+
+ '__repr__': function () {
+ return "[" + this.NAME + " " + this.VERSION + "]";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'toString': function () {
+ return this.__repr__();
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'locale': function() {
+ return {
+ 'amDesignation': Clipperz.PM.Strings.getValue('calendarStrings.amDesignation'),
+ 'pmDesignation': Clipperz.PM.Strings.getValue('calendarStrings.pmDesignation'),
+ 'days': Clipperz.PM.Strings.getValue('calendarStrings.days'),
+ 'shortDays': Clipperz.PM.Strings.getValue('calendarStrings.shortDays'),
+ 'shortMonths': Clipperz.PM.Strings.getValue('calendarStrings.shortMonths'),
+ 'months': Clipperz.PM.Strings.getValue('calendarStrings.months')
+ }
+ },
+
+ //=========================================================================
+/*
+ 'formatDateWithPHPLikeTemplate': function(aDate, aTemplate) {
+ return Clipperz.Date.formatDateWithPHPLikeTemplateAndLocale(aDate, aTemplate, Clipperz.PM.Date.locale());
+ },
+
+ 'parseDateWithPHPLikeTemplate': function(aDate, aTemplate) {
+ return Clipperz.Date.parseDateWithPHPTemplateAndLocale(aDate, aTemplate, Clipperz.PM.Date.locale());
+ },
+
+ //=========================================================================
+
+ 'formatDateWithJavaLikeTemplate': function(aDate, aTemplate) {
+ return Clipperz.Date.formatDateWithJavaLikeTemplateAndLocale(aDate, aTemplate, Clipperz.PM.Date.locale());
+ },
+
+ 'parseDateWithJavaLikeTemplate': function(aDate, aTemplate) {
+ return Clipperz.Date.parseDateWithJavaLikeTemplateAndLocale(aDate, aTemplate, Clipperz.PM.Date.locale());
+ },
+*/
+ //=========================================================================
+
+ 'formatWithTemplate': function (aTemplate, aDate) {
+ return Clipperz.PM.Date.formatDateWithTemplate(aDate, aTemplate);
+ },
+
+ 'formatDateWithTemplate': function(aDate, aTemplate) {
+ var result;
+
+ if (aDate == null) {
+ result = ""
+ } else {
+ result = Clipperz.Date.formatDateWithPHPLikeTemplateAndLocale(aDate, aTemplate, Clipperz.PM.Date.locale());
+ };
+
+ return result;
+ },
+
+ 'parseDateWithTemplate': function(aValue, aTemplate) {
+ return Clipperz.Date.parseDateWithPHPTemplateAndLocale(aValue, aTemplate, Clipperz.PM.Date.locale());
+ },
+
+ //=========================================================================
+
+ 'formatDateWithUTCFormat': function(aDate) {
+ return Clipperz.Date.formatDateWithUTCFormatAndLocale(aDate, Clipperz.PM.Date.locale());
+ },
+
+ 'parseDateWithUTCFormat': function(aValue) {
+ var result;
+
+ if (aValue == null) {
+ result = null;
+ } else {
+ result = Clipperz.Date.parseDateWithUTCFormatAndLocale(aValue, Clipperz.PM.Date.locale());
+ }
+
+ return result;
+ },
+
+ //=========================================================================
+
+ 'getElapsedTimeDescription': function(aDate) {
+ var result;
+
+ result = ""
+
+ if (aDate != null) {
+ var now;
+ var elapsedTime;
+
+ var millisencondsInAMinute;
+ var millisencondsInAnHour;
+ var millisencondsInADay;
+ var millisencondsInAWeek;
+ var millisencondsInAMonth;
+
+ now = new Date();
+ elapsedTime = now.getTime() - aDate.getTime();
+
+ millisencondsInAMinute = 60 * 1000;
+ millisencondsInAnHour = millisencondsInAMinute * 60;
+ millisencondsInADay = millisencondsInAnHour * 24;
+ millisencondsInAWeek = millisencondsInADay * 7;
+ millisencondsInAMonth = millisencondsInAWeek * 5;
+
+ if ((elapsedTime / millisencondsInAMonth) > 1) {
+ result = Clipperz.PM.Strings.getValue('elapsedTimeDescriptions.MORE_THAN_A_MONTH_AGO');
+ } else if ((elapsedTime / millisencondsInAWeek) > 1) {
+ var elapsedWeeks;
+
+ elapsedWeeks = Math.floor((elapsedTime / millisencondsInAWeek));
+ if (elapsedWeeks == 1) {
+ result = Clipperz.PM.Strings.getValue('elapsedTimeDescriptions.MORE_THAN_A_WEEK_AGO');
+ } else {
+ result = Clipperz.PM.Strings.getValue('elapsedTimeDescriptions.MORE_THAN_*_WEEKS_AGO').replace(/__elapsed__/, elapsedWeeks);
+ }
+ } else if ((elapsedTime / millisencondsInADay) > 1) {
+ var elapsedDays;
+
+ elapsedDays = Math.floor((elapsedTime / millisencondsInADay));
+ if (elapsedDays == 1) {
+ result = Clipperz.PM.Strings.getValue('elapsedTimeDescriptions.YESTERDAY');
+ } else {
+ result = Clipperz.PM.Strings.getValue('elapsedTimeDescriptions.*_DAYS_AGO').replace(/__elapsed__/, elapsedDays);
+ }
+ } else if ((elapsedTime / millisencondsInAnHour) > 1) {
+ var elapsedHours;
+
+ elapsedHours = Math.floor((elapsedTime / millisencondsInAnHour));
+ if (elapsedHours == 1) {
+ result = Clipperz.PM.Strings.getValue('elapsedTimeDescriptions.ABOUT_AN_HOUR_AGO');
+ } else {
+ result = Clipperz.PM.Strings.getValue('elapsedTimeDescriptions.*_HOURS_AGO').replace(/__elapsed__/, elapsedHours);
+ }
+ } else {
+ var elapsed10Minutes;
+
+ elapsed10Minutes = (Math.floor((elapsedTime / millisencondsInAMinute) / 10)) * 10;
+ if (elapsed10Minutes == 0) {
+ result = Clipperz.PM.Strings.getValue('elapsedTimeDescriptions.JUST_A_FEW_MINUTES_AGO');
+ } else {
+ result = Clipperz.PM.Strings.getValue('elapsedTimeDescriptions.ABOUT_*_MINUTES_AGO').replace(/__elapsed__/, elapsed10Minutes+"");
+ }
+ }
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'parse': function (aValue) {
+ return Clipperz.PM.Date.parseDateWithUTCFormat(aValue);
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/PIN.js b/frontend/delta/js/Clipperz/PM/PIN.js
new file mode 100644
index 0000000..a32889a
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/PIN.js
@@ -0,0 +1,132 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.PIN) == 'undefined') { Clipperz.PM.PIN = {}; }
+
+MochiKit.Base.update(Clipperz.PM.PIN, {
+
+ //-------------------------------------------------------------------------
+
+ '__repr__': function () {
+ return "[" + this.NAME + " " + this.VERSION + "]";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'toString': function () {
+ return this.__repr__();
+ },
+
+ 'CREDENTIALS': 'CLIPPERZ.CREDENTIALS',
+ 'FAILURE_COUNT': 'CLIPPERZ.FAILED_LOGIN_COUNT',
+ 'ALLOWED_RETRY': 3,
+
+ //-------------------------------------------------------------------------
+
+ 'isSet': function () {
+ return (this.storedCredentials() != null);
+ },
+
+ 'storedCredentials': function () {
+ return localStorage[this.CREDENTIALS];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'recordFailedAttempt': function () {
+ var failureCount;
+ var result;
+
+ failureCount = localStorage[this.FAILURE_COUNT];
+
+ if (failureCount == null) {
+ failureCount = 0
+ }
+
+ failureCount ++;
+
+ if (failureCount < this.ALLOWED_RETRY) {
+ localStorage[this.FAILURE_COUNT] = failureCount;
+ result = failureCount;
+ } else {
+ this.removeLocalCredentials();
+ result = -1;
+ }
+
+ return result;
+ },
+
+ 'resetFailedAttemptCount': function () {
+ localStorage.removeItem(this.FAILURE_COUNT);
+ },
+
+ 'failureCount': function () {
+ return localStorage[this.FAILURE_COUNT];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'deriveKeyFromPin': function (aPIN) {
+ return Clipperz.Crypto.SHA.sha256(new Clipperz.ByteArray(aPIN));
+ },
+
+ 'credentialsWithPIN': function (aPIN) {
+ var byteArrayValue;
+ var decryptedValue;
+ var result;
+
+ byteArrayValue = (new Clipperz.ByteArray()).appendBase64String(localStorage[this.CREDENTIALS]);
+ decryptedValue = Clipperz.Crypto.AES.decrypt(this.deriveKeyFromPin(aPIN), byteArrayValue).asString();
+ try {
+ result = Clipperz.Base.evalJSON(decryptedValue);
+ } catch (error) {
+ result = {'username':'fakeusername', 'passphrase':'fakepassphrase'};
+ }
+
+ return result;
+ },
+
+ 'setCredentialsWithPIN': function (aPIN, someCredentials) {
+ var encodedValue;
+ var byteArrayValue;
+ var encryptedValue;
+
+ encodedValue = Clipperz.Base.serializeJSON(someCredentials);
+ byteArrayValue = new Clipperz.ByteArray(encodedValue);
+ encryptedValue = Clipperz.Crypto.AES.encrypt(this.deriveKeyFromPin(aPIN), byteArrayValue).toBase64String();
+
+ localStorage[this.CREDENTIALS] = encryptedValue;
+ },
+
+ 'removeLocalCredentials': function () {
+ localStorage.removeItem(this.CREDENTIALS);
+ localStorage.removeItem(this.FAILURE_COUNT);
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/Proxy.js b/frontend/delta/js/Clipperz/PM/Proxy.js
new file mode 100644
index 0000000..2ac684a
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Proxy.js
@@ -0,0 +1,186 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+
+//=============================================================================
+
+Clipperz.PM.Proxy = function(args) {
+ args = args || {};
+
+ this._shouldPayTolls = args.shouldPayTolls || false;
+
+ this._tolls = {
+ 'CONNECT': [],
+ 'REGISTER': [],
+ 'MESSAGE': []
+ };
+
+ if (args.isDefault === true) {
+ Clipperz.PM.Proxy.defaultProxy = this;
+ }
+
+ return this;
+}
+
+Clipperz.PM.Proxy.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function() {
+ return "Clipperz.PM.Proxy";
+ },
+
+ //=========================================================================
+
+ 'shouldPayTolls': function() {
+ return this._shouldPayTolls;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'tolls': function() {
+ return this._tolls;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'payToll': function(aRequestType, someParameters) {
+ var deferredResult;
+
+ if (this.shouldPayTolls()) {
+ deferredResult = new Clipperz.Async.Deferred("Proxy.payToll", {trace:false});
+
+ if (this.tolls()[aRequestType].length == 0) {
+ deferredResult.addMethod(this, 'sendMessage', 'knock', {requestType:aRequestType});
+ deferredResult.addMethod(this, 'setTollCallback');
+ }
+ deferredResult.addMethod(this.tolls()[aRequestType], 'pop');
+ deferredResult.addCallback(MochiKit.Base.methodcaller('deferredPay'));
+ deferredResult.addCallback(function(aToll) {
+ var result;
+
+ result = {
+ parameters: someParameters,
+ toll: aToll
+ }
+
+ return result;
+ });
+
+ deferredResult.callback();
+ } else {
+ deferredResult = MochiKit.Async.succeed({parameters:someParameters});
+ }
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'addToll': function(aToll) {
+ this.tolls()[aToll.requestType()].push(aToll);
+ },
+
+ //=========================================================================
+
+ 'setTollCallback': function(someParameters) {
+ if (typeof(someParameters['toll']) != 'undefined') {
+ this.addToll(new Clipperz.PM.Toll(someParameters['toll']));
+ }
+
+ return someParameters['result'];
+ },
+
+ //=========================================================================
+
+ 'registration': function (someParameters) {
+ return this.processMessage('registration', someParameters, 'REGISTER');
+ },
+
+ 'handshake': function (someParameters) {
+ return this.processMessage('handshake', someParameters, 'CONNECT');
+ },
+
+ 'message': function (someParameters) {
+ return this.processMessage('message', someParameters, 'MESSAGE');
+ },
+
+ 'logout': function (someParameters) {
+ return this.processMessage('logout', someParameters, 'MESSAGE');
+ },
+
+ //=========================================================================
+
+ 'processMessage': function (aFunctionName, someParameters, aRequestType) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("Proxy.processMessage", {trace:false});
+ deferredResult.addMethod(this, 'payToll', aRequestType);
+ deferredResult.addMethod(this, 'sendMessage', aFunctionName);
+ deferredResult.addMethod(this, 'setTollCallback');
+ deferredResult.callback(someParameters);
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ '_sendMessage': function (aFunctionName, aVersion, someParameters) {
+ throw Clipperz.Base.exception.AbstractMethod;
+ },
+
+ 'sendMessage': function (aFunctionName, someParameters) {
+ var deferredResult;
+
+// TODO: read actual application version for a property set at build time
+ deferredResult = new Clipperz.Async.Deferred("Proxy.sendMessage", {trace:false});
+ deferredResult.addMethod(this, '_sendMessage', aFunctionName, 'fake-app-version');
+ deferredResult.addErrback(MochiKit.Base.method(this, 'handleError'));
+ deferredResult.callback(someParameters);
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'handleError': function (anError) {
+ if (anError['message'] == 'Wrong application version') {
+ anError['isPermanent'] = true;
+ }
+ return anError;
+ },
+
+ //=========================================================================
+
+ 'isReadOnly': function () {
+ return false;
+ },
+
+ 'canRegisterNewUsers': function () {
+ return true;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+
+});
diff --git a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js
new file mode 100755
index 0000000..1638d99
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js
@@ -0,0 +1,86 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+
+//=============================================================================
+
+Clipperz.PM.Proxy.JSON = function(args) {
+ Clipperz.PM.Proxy.JSON.superclass.constructor.call(this, args);
+
+ this._url = args.url || Clipperz.Base.exception.raise('MandatoryParameter');
+
+ return this;
+}
+
+Clipperz.Base.extend(Clipperz.PM.Proxy.JSON, Clipperz.PM.Proxy, {
+
+ 'toString': function() {
+ return "Clipperz.PM.Proxy.JSON";
+ },
+
+ //=========================================================================
+
+ 'url': function () {
+ return this._url;
+ },
+
+ //=========================================================================
+
+ '_sendMessage': function(aFunctionName, aVersion, someParameters) {
+ var deferredResult;
+ var parameters;
+
+ parameters = {
+ method: aFunctionName,
+ version: aVersion,
+ parameters: Clipperz.Base.serializeJSON(someParameters)
+ };
+
+ deferredResult = new Clipperz.Async.Deferred("Proxy.JSON.sendMessage", {trace:false});
+ deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'remoteRequestSent');
+ deferredResult.addCallback(MochiKit.Async.doXHR, this.url(), {
+ method:'POST',
+ sendContent:MochiKit.Base.queryString(parameters),
+ headers:{"Content-Type":"application/x-www-form-urlencoded"}
+ });
+ deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'remoteRequestReceived');
+ deferredResult.addCallback(MochiKit.Base.itemgetter('responseText'));
+ deferredResult.addCallback(Clipperz.Base.evalJSON);
+ deferredResult.addCallback(function (someValues) {
+ if (someValues['result'] == 'EXCEPTION') {
+ throw someValues['message'];
+ }
+
+ return someValues;
+ })
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+
+});
diff --git a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js
new file mode 100644
index 0000000..5711742
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.DataStore.js
@@ -0,0 +1,793 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.Proxy.Offline) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.Proxy.Offline.DataStore depends on Clipperz.PM.Proxy.Offline!";
+}
+
+//=============================================================================
+
+Clipperz.PM.Proxy.Offline.DataStore = function(args) {
+ args = args || {};
+
+ this._data = args.data || (typeof(_clipperz_dump_data_) != 'undefined' ? _clipperz_dump_data_ : null);
+ this._isReadOnly = (typeof(args.readOnly) == 'undefined' ? true : args.readOnly);
+ this._shouldPayTolls = args.shouldPayTolls || false;
+
+ this._tolls = {};
+ this._currentStaticConnection = null;
+
+ return this;
+}
+
+Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.DataStore, Object, {
+
+ //-------------------------------------------------------------------------
+
+ 'isReadOnly': function () {
+ return this._isReadOnly;
+ },
+
+ 'canRegisterNewUsers': function () {
+ return false;
+ },
+
+
+ //-------------------------------------------------------------------------
+
+ 'shouldPayTolls': function() {
+ return this._shouldPayTolls;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'data': function () {
+ return this._data;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'tolls': function () {
+ return this._tolls;
+ },
+
+ //=========================================================================
+
+ 'resetData': function() {
+ this._data = {
+ 'users': {
+ 'catchAllUser': {
+ __masterkey_test_value__: 'masterkey',
+ s: '112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00',
+ v: '112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00'
+ }
+ }
+ };
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setupWithEncryptedData': function(someData) {
+ this._data = Clipperz.Base.deepClone(someData);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setupWithData': function(someData) {
+ var deferredResult;
+ var resultData;
+ var i, c;
+
+//Clipperz.log(">>> Proxy.Test.setupWithData");
+ resultData = this._data;
+
+ deferredResult = new Clipperz.Async.Deferred("Proxy.Test.seupWithData", {trace:false});
+ c = someData['users'].length;
+
+ for (i=0; i<c; i++) {
+ var newConnection;
+ var recordConfiguration;
+
+ deferredResult.addMethod(this, 'userSerializedEncryptedData', someData['users'][i]);
+ deferredResult.addCallback(MochiKit.Base.bind(function(aUserSerializationContext) {
+ resultData['users'][aUserSerializationContext['credentials']['C']] = {
+ 's': aUserSerializationContext['credentials']['s'],
+ 'v': aUserSerializationContext['credentials']['v'],
+ 'version': aUserSerializationContext['data']['connectionVersion'],
+ 'userDetails': aUserSerializationContext['encryptedData']['user']['header'],
+ 'userDetailsVersion': aUserSerializationContext['encryptedData']['user']['version'],
+ 'statistics': aUserSerializationContext['encryptedData']['user']['statistics'],
+ 'lock': aUserSerializationContext['encryptedData']['user']['lock'],
+ 'records': this.rearrangeRecordsData(aUserSerializationContext['encryptedData']['records'])
+ }
+ }, this));
+ }
+
+ deferredResult.addCallback(MochiKit.Base.bind(function() {
+ this._data = resultData;
+ }, this));
+
+ deferredResult.callback();
+//Clipperz.log("<<< Proxy.Test.setupWithData");
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'getTollForRequestType': function (aRequestType) {
+ var result;
+ var targetValue;
+ var cost;
+
+ targetValue = Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(32).toHexString().substring(2);
+ switch (aRequestType) {
+ case 'REGISTER':
+ cost = 5;
+ break;
+ case 'CONNECT':
+ cost = 5;
+ break;
+ case 'MESSAGE':
+ cost = 2;
+ break;
+ }
+
+ result = {
+ requestType: aRequestType,
+ targetValue: targetValue,
+ cost: cost
+ }
+
+ if (this.shouldPayTolls()) {
+ this.tolls()[targetValue] = result;
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'checkToll': function (aFunctionName, someParameters) {
+ if (this.shouldPayTolls()) {
+ var localToll;
+ var tollParameters;
+
+ tollParameters = someParameters['toll'];
+ localToll = this.tolls()[tollParameters['targetValue']];
+
+ if (localToll != null) {
+ if (! Clipperz.PM.Toll.validate(tollParameters['targetValue'], tollParameters['toll'], localToll['cost'])) {
+ throw "Toll value too low.";
+ };
+ } else {
+ throw "Missing toll";
+ }
+ }
+ },
+
+ //=========================================================================
+
+ 'currentStaticConnection': function () {
+ if (this._currentStaticConnection == null) {
+ this._currentStaticConnection = {};
+ }
+
+ return this._currentStaticConnection;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getConnectionForRequest': function (aFunctionName, someParameters) {
+ var result;
+
+ if (this.shouldPayTolls()) {
+ if ((typeof(someParameters['toll']) != 'undefined') && (typeof(someParameters['toll']['targetValue']) != 'undefined')) {
+ result = this.tolls()[someParameters['toll']['targetValue']]['connection'];
+ if (typeof(result) == 'undefined') {
+ result = {};
+ }
+ } else {
+ result = {};
+ }
+ } else {
+ result = this.currentStaticConnection();
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'storeConnectionForRequestWithConnectionAndResponse': function (aFunctionName, someParameters, aConnection, aResponse) {
+ if (this.shouldPayTolls()) {
+ if ((typeof(aResponse['toll']) != 'undefined')
+ && (typeof(aResponse['toll']['targetValue']) != 'undefined')
+ && (typeof(this.tolls()[aResponse['toll']['targetValue']]) != 'undefined')
+ ) {
+ this.tolls()[aResponse['toll']['targetValue']]['connection'] = aConnection;
+ }
+ }
+ },
+
+ //=========================================================================
+
+ 'processMessage': function (aFunctionName, someParameters) {
+ var result;
+ var connection;
+
+ connection = this.getConnectionForRequest(aFunctionName, someParameters);
+
+ switch(aFunctionName) {
+ case 'knock':
+ result = this._knock(connection, someParameters);
+ break;
+ case 'registration':
+ this.checkToll(aFunctionName, someParameters);
+ result = this._registration(connection, someParameters.parameters);
+ break;
+ case 'handshake':
+ this.checkToll(aFunctionName, someParameters);
+ result = this._handshake(connection, someParameters.parameters);
+ break;
+ case 'message':
+ this.checkToll(aFunctionName, someParameters);
+ result = this._message(connection, someParameters.parameters);
+ break;
+ case 'logout':
+ this._currentStaticConnection = null;
+ result = this._logout(connection, someParameters.parameters);
+ break;
+ }
+
+ this.storeConnectionForRequestWithConnectionAndResponse(aFunctionName, someParameters, connection, result);
+
+ return MochiKit.Async.succeed(result);
+ },
+
+ //=========================================================================
+
+ '_knock': function(aConnection, someParameters) {
+ var result;
+
+ result = {
+ toll: this.getTollForRequestType(someParameters['requestType'])
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_registration': function(aConnection, someParameters) {
+ if (this.isReadOnly() == false) {
+ if (typeof(this.data()['users'][someParameters['credentials']['C']]) == 'undefined') {
+ this.data()['users'][someParameters['credentials']['C']] = {
+ 's': someParameters['credentials']['s'],
+ 'v': someParameters['credentials']['v'],
+ 'version': someParameters['credentials']['version'],
+// 'lock': Clipperz.Crypto.Base.generateRandomSeed(),
+ 'userDetails': someParameters['user']['header'],
+ 'statistics': someParameters['user']['statistics'],
+ 'userDetailsVersion': someParameters['user']['version'],
+ 'records': {}
+ }
+ } else {
+ throw "user already exists";
+ }
+ } else {
+ throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
+ }
+
+ result = {
+ result: {
+ 'lock': this.data()['users'][someParameters['credentials']['C']]['lock'],
+ 'result': 'done'
+ },
+ toll: this.getTollForRequestType('CONNECT')
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_handshake': function(aConnection, someParameters) {
+ var result;
+ var nextTollRequestType;
+
+ result = {};
+ if (someParameters.message == "connect") {
+ var userData;
+ var randomBytes;
+ var v;
+
+ userData = this.data()['users'][someParameters.parameters.C];
+
+ if ((typeof(userData) != 'undefined') && (userData['version'] == someParameters.version)) {
+ aConnection['userData'] = userData;
+ aConnection['C'] = someParameters.parameters.C;
+ } else {
+ aConnection['userData'] = this.data()['users']['catchAllUser'];
+ }
+
+ randomBytes = Clipperz.Crypto.Base.generateRandomSeed();
+ aConnection['b'] = new Clipperz.Crypto.BigInt(randomBytes, 16);
+ v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16);
+ aConnection['B'] = v.add(Clipperz.Crypto.SRP.g().powerModule(aConnection['b'], Clipperz.Crypto.SRP.n()));
+
+ aConnection['A'] = someParameters.parameters.A;
+
+ result['s'] = aConnection['userData']['s'];
+ result['B'] = aConnection['B'].asString(16);
+
+ nextTollRequestType = 'CONNECT';
+ } else if (someParameters.message == "credentialCheck") {
+ var v, u, S, A, K, M1;
+
+ v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16);
+ u = new Clipperz.Crypto.BigInt(Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(aConnection['B'].asString(10))).toHexString(), 16);
+ A = new Clipperz.Crypto.BigInt(aConnection['A'], 16);
+ S = (A.multiply(v.powerModule(u, Clipperz.Crypto.SRP.n()))).powerModule(aConnection['b'], Clipperz.Crypto.SRP.n());
+
+ K = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(S.asString(10))).toHexString().slice(2);
+
+ M1 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + aConnection['B'].asString(10) + K)).toHexString().slice(2);
+ if (someParameters.parameters.M1 == M1) {
+ var M2;
+
+ M2 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + someParameters.parameters.M1 + K)).toHexString().slice(2);
+ result['M2'] = M2;
+ } else {
+ throw new Error("Client checksum verification failed! Expected <" + M1 + ">, received <" + someParameters.parameters.M1 + ">.", "Error");
+ }
+
+ nextTollRequestType = 'MESSAGE';
+ } else if (someParameters.message == "oneTimePassword") {
+ var otpData;
+
+ otpData = this.data()['onetimePasswords'][someParameters.parameters.oneTimePasswordKey];
+
+ try {
+ if (typeof(otpData) != 'undefined') {
+ if (otpData['status'] == 'ACTIVE') {
+ if (otpData['key_checksum'] == someParameters.parameters.oneTimePasswordKeyChecksum) {
+ result = {
+ 'data': otpData['data'],
+ 'version': otpData['version']
+ }
+
+ otpData['status'] = 'REQUESTED';
+ } else {
+ otpData['status'] = 'DISABLED';
+ throw "The requested One Time Password has been disabled, due to a wrong keyChecksum";
+ }
+ } else {
+ throw "The requested One Time Password was not active";
+ }
+ } else {
+ throw "The requested One Time Password has not been found"
+ }
+ } catch (exception) {
+ result = {
+ 'data': Clipperz.PM.Crypto.randomKey(),
+ 'version': Clipperz.PM.Connection.communicationProtocol.currentVersion
+ }
+ }
+ nextTollRequestType = 'CONNECT';
+ } else {
+ Clipperz.logError("Clipperz.PM.Proxy.Test.handshake - unhandled message: " + someParameters.message);
+ }
+
+ result = {
+ result: result,
+ toll: this.getTollForRequestType(nextTollRequestType)
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_message': function(aConnection, someParameters) {
+ var result;
+
+ result = {};
+
+ //=====================================================================
+ //
+ // R E A D - O N L Y M e t h o d s
+ //
+ //=====================================================================
+ if (someParameters.message == 'getUserDetails') {
+ var recordsStats;
+ var recordReference;
+
+ recordsStats = {};
+ for (recordReference in aConnection['userData']['records']) {
+ recordsStats[recordReference] = {
+ 'updateDate': aConnection['userData']['records'][recordReference]['updateDate']
+ }
+ }
+
+ result['header'] = this.userDetails(aConnection);
+ result['statistics'] = this.statistics(aConnection);
+ result['maxNumberOfRecords'] = aConnection['userData']['maxNumberOfRecords'];
+ result['version'] = aConnection['userData']['userDetailsVersion'];
+ result['recordsStats'] = recordsStats;
+
+ if (this.isReadOnly() == false) {
+ var lock;
+
+ if (typeof(aConnection['userData']['lock']) == 'undefined') {
+ aConnection['userData']['lock'] = "<<LOCK>>";
+ }
+
+ result['lock'] = aConnection['userData']['lock'];
+ }
+
+ //=====================================================================
+ } else if (someParameters.message == 'getRecordDetail') {
+/*
+ var recordData;
+ var currentVersionData;
+
+ recordData = this.userData()['records'][someParameters['parameters']['reference']];
+ result['reference'] = someParameters['parameters']['reference'];
+ result['data'] = recordData['data'];
+ result['version'] = recordData['version'];
+ result['creationData'] = recordData['creationDate'];
+ result['updateDate'] = recordData['updateDate'];
+ result['accessDate'] = recordData['accessDate'];
+
+ currentVersionData = recordData['versions'][recordData['currentVersion']];
+
+ result['currentVersion'] = {};
+ result['currentVersion']['reference'] = recordData['currentVersion'];
+ result['currentVersion']['version'] = currentVersionData['version'];
+ result['currentVersion']['header'] = currentVersionData['header'];
+ result['currentVersion']['data'] = currentVersionData['data'];
+ result['currentVersion']['creationData'] = currentVersionData['creationDate'];
+ result['currentVersion']['updateDate'] = currentVersionData['updateDate'];
+ result['currentVersion']['accessDate'] = currentVersionData['accessDate'];
+ if (typeof(currentVersionData['previousVersion']) != 'undefined') {
+ result['currentVersion']['previousVersionKey'] = currentVersionData['previousVersionKey'];
+ result['currentVersion']['previousVersion'] = currentVersionData['previousVersion'];
+ }
+*/
+ MochiKit.Base.update(result, aConnection['userData']['records'][someParameters['parameters']['reference']]);
+ result['reference'] = someParameters['parameters']['reference'];
+
+ //=====================================================================
+ //
+ // R E A D - W R I T E M e t h o d s
+ //
+ //=====================================================================
+ } else if (someParameters.message == 'upgradeUserCredentials') {
+ if (this.isReadOnly() == false) {
+ var parameters;
+ var credentials;
+
+ parameters = someParameters['parameters'];
+ credentials = parameters['credentials'];
+
+ if ((credentials['C'] == null)
+ || (credentials['s'] == null)
+ || (credentials['v'] == null)
+ || (credentials['version'] != Clipperz.PM.Connection.communicationProtocol.currentVersion)
+ ) {
+ result = Clipperz.PM.DataModel.User.exception.CredentialUpgradeFailed;
+ } else {
+ var oldCValue;
+ oldCValue = aConnection['C'];
+
+ this.data()['users'][credentials['C']] = aConnection['userData'];
+ aConnection['C'] = credentials['C'];
+
+ aConnection['userData']['s'] = credentials['s'];
+ aConnection['userData']['v'] = credentials['v'];
+ aConnection['userData']['version'] = credentials['version'];
+
+ aConnection['userData']['userDetails'] = parameters['user']['header'];
+ aConnection['userData']['userDetailsVersion'] = parameters['user']['version'];
+ aConnection['userData']['statistics'] = parameters['user']['statistics'];
+
+ aConnection['userData']['lock'] = parameters['user']['lock'];
+
+ delete this.data()['users'][oldCValue];
+
+ result = {result:"done", parameters:parameters};
+ }
+ } else {
+ throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
+ }
+ //=====================================================================
+/* } else if (someParameters.message == 'updateData') {
+ if (this.isReadOnly() == false) {
+ var i, c;
+
+ if (this.userData()['lock'] != someParameters['parameters']['user']['lock']) {
+ throw "the lock attribute is not processed correctly"
+ }
+
+ this.userData()['userDetails'] = someParameters['parameters']['user']['header'];
+ this.userData()['statistics'] = someParameters['parameters']['user']['statistics'];
+ this.userData()['userDetailsVersions'] = someParameters['parameters']['user']['version'];
+
+ c = someParameters['parameters']['records'].length;
+ for (i=0; i<c; i++) {
+ var currentRecord;
+ var currentRecordData;
+
+ currentRecordData = someParameters['parameters']['records'][i];
+ currentRecord = this.userData()['records'][currentRecordData['record']['reference']];
+
+ if (currentRecord == null) {
+ }
+
+ currentRecord['data'] = currentRecordData['record']['data'];
+ currentRecord['version'] = currentRecordData['record']['version'];
+ currentRecord['currentVersion'] = currentRecordData['currentRecordVersion']['reference'];
+
+ currentRecord['versions'][currentRecordData['currentRecordVersion']['reference']] = {
+ 'data': currentRecordData['currentRecordVersion']['data'],
+ 'version': currentRecordData['currentRecordVersion']['version'],
+ 'previousVersion': currentRecordData['currentRecordVersion']['previousVersion'],
+ 'previousVersionKey': currentRecordData['currentRecordVersion']['previousVersionKey']
+ }
+ }
+
+ this.userData()['lock'] = Clipperz.PM.Crypto.randomKey();
+ result['lock'] = this.userData()['lock'];
+ result['result'] = 'done';
+ } else {
+ throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
+ }
+*/ //=====================================================================
+ } else if (someParameters.message == 'saveChanges') {
+ if (this.isReadOnly() == false) {
+ var i, c;
+
+ if (aConnection['userData']['lock'] != someParameters['parameters']['user']['lock']) {
+ throw "the lock attribute is not processed correctly"
+ }
+
+ aConnection['userData']['userDetails'] = someParameters['parameters']['user']['header'];
+ aConnection['userData']['statistics'] = someParameters['parameters']['user']['statistics'];
+ aConnection['userData']['userDetailsVersion'] = someParameters['parameters']['user']['version'];
+
+ c = someParameters['parameters']['records']['updated'].length;
+ for (i=0; i<c; i++) {
+ var currentRecord;
+ var currentRecordData;
+
+ currentRecordData = someParameters['parameters']['records']['updated'][i];
+ currentRecord = aConnection['userData']['records'][currentRecordData['record']['reference']];
+
+ if (
+ (typeof(aConnection['userData']['records'][currentRecordData['record']['reference']]) == 'undefined')
+ &&
+ (typeof(currentRecordData['currentRecordVersion']) == 'undefined')
+ ) {
+ throw "Record added without a recordVersion";
+ }
+
+ if (currentRecord == null) {
+ currentRecord = {};
+ currentRecord['versions'] = {};
+ currentRecord['creationDate'] = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
+ currentRecord['accessDate'] = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
+
+ aConnection['userData']['records'][currentRecordData['record']['reference']] = currentRecord;
+ }
+
+ currentRecord['data'] = currentRecordData['record']['data'];
+ currentRecord['version'] = currentRecordData['record']['version'];
+ currentRecord['updateDate'] = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
+
+ if (typeof(currentRecordData['currentRecordVersion']) != 'undefined') {
+ currentRecord['currentVersion'] = currentRecordData['currentRecordVersion']['reference'];
+ currentRecord['versions'][currentRecordData['currentRecordVersion']['reference']] = {
+ 'data': currentRecordData['currentRecordVersion']['data'],
+ 'version': currentRecordData['currentRecordVersion']['version'],
+ 'previousVersion': currentRecordData['currentRecordVersion']['previousVersion'],
+ 'previousVersionKey': currentRecordData['currentRecordVersion']['previousVersionKey'],
+ 'creationDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date()),
+ 'updateDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date()),
+ 'accessDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date())
+ }
+ }
+ }
+
+ c = someParameters['parameters']['records']['deleted'].length;
+ for (i=0; i<c; i++) {
+ var currentRecordReference;
+
+ currentRecordReference = someParameters['parameters']['records']['deleted'][i];
+ delete aConnection['userData']['records'][currentRecordReference];
+ }
+
+ aConnection['userData']['lock'] = Clipperz.PM.Crypto.randomKey();
+ result['lock'] = aConnection['userData']['lock'];
+ result['result'] = 'done';
+ } else {
+ throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
+ }
+
+ //=====================================================================
+ //
+ // U N H A N D L E D M e t h o d
+ //
+ //=====================================================================
+ } else {
+ Clipperz.logError("Clipperz.PM.Proxy.Test.message - unhandled message: " + someParameters.message);
+ }
+
+ result = {
+ result: result,
+ toll: this.getTollForRequestType('MESSAGE')
+ }
+
+// return MochiKit.Async.succeed(result);
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_logout': function(someParameters) {
+// return MochiKit.Async.succeed({result: 'done'});
+ return {result: 'done'};
+ },
+
+ //=========================================================================
+ //#########################################################################
+
+ 'isTestData': function(aConnection) {
+ return (typeof(aConnection['userData']['__masterkey_test_value__']) != 'undefined');
+ },
+
+ 'userDetails': function(aConnection) {
+ var result;
+
+ if (this.isTestData(aConnection)) {
+ var serializedHeader;
+ var version;
+
+//Clipperz.logDebug("### test data");
+ version = aConnection['userData']['userDetailsVersion'];
+ serializedHeader = Clipperz.Base.serializeJSON(aConnection['userData']['userDetails']);
+ result = Clipperz.PM.Crypto.encryptingFunctions.versions[version].encrypt(aConnection['userData']['__masterkey_test_value__'], serializedHeader);
+ } else {
+//Clipperz.logDebug("### NOT test data");
+ result = aConnection['userData']['userDetails'];
+ }
+
+ return result;
+ },
+
+ 'statistics': function(aConnection) {
+ var result;
+
+ if (aConnection['userData']['statistics'] != null) {
+ if (this.isTestData(aConnection)) {
+ var serializedStatistics;
+ var version;
+
+ version = aConnection['userData']['userDetailsVersion'];
+ serializedStatistics = Clipperz.Base.serializeJSON(aConnection['userData']['statistics']);
+ result = Clipperz.PM.Crypto.encryptingFunctions.versions[version].encrypt(aConnection['userData']['__masterkey_test_value__'], serializedStatistics);
+ } else {
+ result = aConnection['userData']['statistics'];
+ }
+ } else {
+ result = null;
+ }
+
+ return result;
+ },
+
+/*
+ 'userSerializedEncryptedData': function(someData) {
+ var deferredResult;
+ var deferredContext;
+
+ deferredContext = { 'data': someData };
+
+ deferredResult = new Clipperz.Async.Deferred('Proxy.Test.serializeUserEncryptedData', {trace:false});
+ deferredResult.addCallback(MochiKit.Base.bind(function(aDeferredContext) {
+ aDeferredContext['user'] = this.createUserUsingConfigurationData(aDeferredContext['data']);
+ return aDeferredContext;
+ }, this));
+ deferredResult.addCallback(function(aDeferredContext) {
+// return aDeferredContext['user'].encryptedDataUsingVersion(aDeferredContext['data']['version']);
+ return aDeferredContext['user'].serializedDataUsingVersion(MochiKit.Base.values(aDeferredContext['user'].records()), aDeferredContext['data']['version']);
+ });
+ deferredResult.addCallback(function(aUserEncryptedData) {
+ deferredContext['encryptedData'] = aUserEncryptedData;
+ return deferredContext;
+ });
+ deferredResult.addCallback(function(aDeferredContext) {
+ var connection;
+
+ connection = new Clipperz.PM.Connection.communicationProtocol.versions[aDeferredContext['data']['connectionVersion']]()
+ aDeferredContext['credentials'] = connection.serverSideUserCredentials(aDeferredContext['user'].username(),aDeferredContext['user'].passphrase());
+
+ return aDeferredContext;
+ });
+
+// deferredResult.addCallback(function(aDeferredContext) {
+// return aDeferredContext['user'].serializedDataUsingVersion(MochiKit.Base.values(aDeferredContext['user'].records()), aDeferredContext['data']['version']);
+// }, deferredContext);
+// deferredResult.addCallback(function(aUserSerializedData) {
+// });
+//
+// deferredResult.addCallback(MochiKit.Async.succeed, deferredContext);
+ deferredResult.callback(deferredContext);
+
+ return deferredResult;
+ },
+
+ 'createUserUsingConfigurationData': function(someData) {
+ var result;
+ var user;
+ var recordLabel;
+
+ user = new Clipperz.PM.DataModel.User();
+ user.initForTests();
+ user.setUsername(someData['username']);
+ user.setPassphrase(someData['passphrase']);
+
+ for (recordLabel in someData['records']) {
+ var recordData;
+ var record;
+ var i, c;
+
+ recordData = someData['records'][recordLabel];
+ record = new Clipperz.PM.DataModel.Record({user:user, label:recordLabel});
+ record.setNotes(recordData['notes']);
+
+ c = recordData['fields'].length;
+ for (i=0; i<c; i++) {
+ var recordField;
+
+ recordField = new Clipperz.PM.DataModel.RecordField();
+ recordField.setLabel(recordData['fields'][i]['name']);
+ recordField.setValue(recordData['fields'][i]['value']);
+ recordField.setType(recordData['fields'][i]['type']);
+ record.addField(recordField);
+ }
+ user.addRecord(record, true);
+ }
+
+ result = user;
+
+ return result;
+ },
+*/
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+Clipperz.PM.Proxy.Offline.DataStore['exception'] = {
+ 'ReadOnly': new MochiKit.Base.NamedError("Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly")
+}; \ No newline at end of file
diff --git a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js
new file mode 100644
index 0000000..a3c238c
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js
@@ -0,0 +1,420 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.Proxy.Offline.DataStore) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.Proxy.Offline.LocalStorageDataStore depends on Clipperz.PM.Proxy.Offline.DataStore!";
+}
+
+//=============================================================================
+
+Clipperz.PM.Proxy.Offline.LocalStorageDataStore = function(args) {
+ args = args || {};
+
+ this._data = args.data || (typeof(_clipperz_dump_data_) != 'undefined' ? _clipperz_dump_data_ : null);
+ this._isReadOnly = (typeof(args.readOnly) == 'undefined' ? true : args.readOnly);
+ this._shouldPayTolls = args.shouldPayTolls || false;
+
+ this._tolls = {};
+ this._currentStaticConnection = null;
+
+// Clipperz.PM.Proxy.Offline.LocalStorageDataStore.superclass.constructor.apply(this, arguments);
+
+ return this;
+}
+
+Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.LocalStorageDataStore, Clipperz.PM.Proxy.Offline.DataStore, {
+
+ //=========================================================================
+
+ '_knock': function(aConnection, someParameters) {
+ var result;
+
+ result = {
+ toll: this.getTollForRequestType(someParameters['requestType'])
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_registration': function(aConnection, someParameters) {
+ throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_handshake': function(aConnection, someParameters) {
+ var result;
+ var nextTollRequestType;
+
+ result = {};
+ if (someParameters.message == "connect") {
+ var userData;
+ var randomBytes;
+ var v;
+
+ userData = this.data()['users'][someParameters.parameters.C];
+
+ if ((typeof(userData) != 'undefined') && (userData['version'] == someParameters.version)) {
+ aConnection['userData'] = userData;
+ aConnection['C'] = someParameters.parameters.C;
+ } else {
+ aConnection['userData'] = this.data()['users']['catchAllUser'];
+ }
+
+ randomBytes = Clipperz.Crypto.Base.generateRandomSeed();
+ aConnection['b'] = new Clipperz.Crypto.BigInt(randomBytes, 16);
+ v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16);
+ aConnection['B'] = v.add(Clipperz.Crypto.SRP.g().powerModule(aConnection['b'], Clipperz.Crypto.SRP.n()));
+
+ aConnection['A'] = someParameters.parameters.A;
+
+ result['s'] = aConnection['userData']['s'];
+ result['B'] = aConnection['B'].asString(16);
+
+ nextTollRequestType = 'CONNECT';
+ } else if (someParameters.message == "credentialCheck") {
+ var v, u, S, A, K, M1;
+
+ v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16);
+ u = new Clipperz.Crypto.BigInt(Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(aConnection['B'].asString(10))).toHexString(), 16);
+ A = new Clipperz.Crypto.BigInt(aConnection['A'], 16);
+ S = (A.multiply(v.powerModule(u, Clipperz.Crypto.SRP.n()))).powerModule(aConnection['b'], Clipperz.Crypto.SRP.n());
+
+ K = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(S.asString(10))).toHexString().slice(2);
+
+ M1 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + aConnection['B'].asString(10) + K)).toHexString().slice(2);
+ if (someParameters.parameters.M1 == M1) {
+ var M2;
+
+ M2 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + someParameters.parameters.M1 + K)).toHexString().slice(2);
+ result['M2'] = M2;
+ } else {
+ throw new Error("Client checksum verification failed! Expected <" + M1 + ">, received <" + someParameters.parameters.M1 + ">.", "Error");
+ }
+
+ nextTollRequestType = 'MESSAGE';
+ } else if (someParameters.message == "oneTimePassword") {
+ var otpData;
+
+ otpData = this.data()['onetimePasswords'][someParameters.parameters.oneTimePasswordKey];
+
+ try {
+ if (typeof(otpData) != 'undefined') {
+ if (otpData['status'] == 'ACTIVE') {
+ if (otpData['key_checksum'] == someParameters.parameters.oneTimePasswordKeyChecksum) {
+ result = {
+ 'data': otpData['data'],
+ 'version': otpData['version']
+ }
+
+ otpData['status'] = 'REQUESTED';
+ } else {
+ otpData['status'] = 'DISABLED';
+ throw "The requested One Time Password has been disabled, due to a wrong keyChecksum";
+ }
+ } else {
+ throw "The requested One Time Password was not active";
+ }
+ } else {
+ throw "The requested One Time Password has not been found"
+ }
+ } catch (exception) {
+ result = {
+ 'data': Clipperz.PM.Crypto.randomKey(),
+ 'version': Clipperz.PM.Connection.communicationProtocol.currentVersion
+ }
+ }
+ nextTollRequestType = 'CONNECT';
+ } else {
+ Clipperz.logError("Clipperz.PM.Proxy.Test.handshake - unhandled message: " + someParameters.message);
+ }
+
+ result = {
+ result: result,
+ toll: this.getTollForRequestType(nextTollRequestType)
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_message': function(aConnection, someParameters) {
+ var result;
+
+ result = {};
+
+ //=====================================================================
+ //
+ // R E A D - O N L Y M e t h o d s
+ //
+ //=====================================================================
+ if (someParameters.message == 'getUserDetails') {
+ var recordsStats;
+ var recordReference;
+
+ recordsStats = {};
+ for (recordReference in aConnection['userData']['records']) {
+ recordsStats[recordReference] = {
+ 'updateDate': aConnection['userData']['records'][recordReference]['updateDate']
+ }
+ }
+
+ result['header'] = this.userDetails(aConnection);
+ result['statistics'] = this.statistics(aConnection);
+ result['maxNumberOfRecords'] = aConnection['userData']['maxNumberOfRecords'];
+ result['version'] = aConnection['userData']['userDetailsVersion'];
+ result['recordsStats'] = recordsStats;
+
+ if (this.isReadOnly() == false) {
+ var lock;
+
+ if (typeof(aConnection['userData']['lock']) == 'undefined') {
+ aConnection['userData']['lock'] = "<<LOCK>>";
+ }
+
+ result['lock'] = aConnection['userData']['lock'];
+ }
+
+ //=====================================================================
+ } else if (someParameters.message == 'getRecordDetail') {
+/*
+ var recordData;
+ var currentVersionData;
+
+ recordData = this.userData()['records'][someParameters['parameters']['reference']];
+ result['reference'] = someParameters['parameters']['reference'];
+ result['data'] = recordData['data'];
+ result['version'] = recordData['version'];
+ result['creationData'] = recordData['creationDate'];
+ result['updateDate'] = recordData['updateDate'];
+ result['accessDate'] = recordData['accessDate'];
+
+ currentVersionData = recordData['versions'][recordData['currentVersion']];
+
+ result['currentVersion'] = {};
+ result['currentVersion']['reference'] = recordData['currentVersion'];
+ result['currentVersion']['version'] = currentVersionData['version'];
+ result['currentVersion']['header'] = currentVersionData['header'];
+ result['currentVersion']['data'] = currentVersionData['data'];
+ result['currentVersion']['creationData'] = currentVersionData['creationDate'];
+ result['currentVersion']['updateDate'] = currentVersionData['updateDate'];
+ result['currentVersion']['accessDate'] = currentVersionData['accessDate'];
+ if (typeof(currentVersionData['previousVersion']) != 'undefined') {
+ result['currentVersion']['previousVersionKey'] = currentVersionData['previousVersionKey'];
+ result['currentVersion']['previousVersion'] = currentVersionData['previousVersion'];
+ }
+*/
+ MochiKit.Base.update(result, aConnection['userData']['records'][someParameters['parameters']['reference']]);
+ result['reference'] = someParameters['parameters']['reference'];
+
+ //=====================================================================
+ //
+ // R E A D - W R I T E M e t h o d s
+ //
+ //=====================================================================
+ } else if (someParameters.message == 'upgradeUserCredentials') {
+ if (this.isReadOnly() == false) {
+ var parameters;
+ var credentials;
+
+ parameters = someParameters['parameters'];
+ credentials = parameters['credentials'];
+
+ if ((credentials['C'] == null)
+ || (credentials['s'] == null)
+ || (credentials['v'] == null)
+ || (credentials['version'] != Clipperz.PM.Connection.communicationProtocol.currentVersion)
+ ) {
+ result = Clipperz.PM.DataModel.User.exception.CredentialUpgradeFailed;
+ } else {
+ var oldCValue;
+ oldCValue = aConnection['C'];
+
+ this.data()['users'][credentials['C']] = aConnection['userData'];
+ aConnection['C'] = credentials['C'];
+
+ aConnection['userData']['s'] = credentials['s'];
+ aConnection['userData']['v'] = credentials['v'];
+ aConnection['userData']['version'] = credentials['version'];
+
+ aConnection['userData']['userDetails'] = parameters['user']['header'];
+ aConnection['userData']['userDetailsVersion'] = parameters['user']['version'];
+ aConnection['userData']['statistics'] = parameters['user']['statistics'];
+
+ aConnection['userData']['lock'] = parameters['user']['lock'];
+
+ delete this.data()['users'][oldCValue];
+
+ result = {result:"done", parameters:parameters};
+ }
+ } else {
+ throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
+ }
+
+ //=====================================================================
+
+ } else if (someParameters.message == 'saveChanges') {
+ if (this.isReadOnly() == false) {
+ var i, c;
+
+ if (aConnection['userData']['lock'] != someParameters['parameters']['user']['lock']) {
+ throw "the lock attribute is not processed correctly"
+ }
+
+ aConnection['userData']['userDetails'] = someParameters['parameters']['user']['header'];
+ aConnection['userData']['statistics'] = someParameters['parameters']['user']['statistics'];
+ aConnection['userData']['userDetailsVersion'] = someParameters['parameters']['user']['version'];
+
+ c = someParameters['parameters']['records']['updated'].length;
+ for (i=0; i<c; i++) {
+ var currentRecord;
+ var currentRecordData;
+
+ currentRecordData = someParameters['parameters']['records']['updated'][i];
+ currentRecord = aConnection['userData']['records'][currentRecordData['record']['reference']];
+
+ if (
+ (typeof(aConnection['userData']['records'][currentRecordData['record']['reference']]) == 'undefined')
+ &&
+ (typeof(currentRecordData['currentRecordVersion']) == 'undefined')
+ ) {
+ throw "Record added without a recordVersion";
+ }
+
+ if (currentRecord == null) {
+ currentRecord = {};
+ currentRecord['versions'] = {};
+ currentRecord['creationDate'] = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
+ currentRecord['accessDate'] = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
+
+ aConnection['userData']['records'][currentRecordData['record']['reference']] = currentRecord;
+ }
+
+ currentRecord['data'] = currentRecordData['record']['data'];
+ currentRecord['version'] = currentRecordData['record']['version'];
+ currentRecord['updateDate'] = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
+
+ if (typeof(currentRecordData['currentRecordVersion']) != 'undefined') {
+ currentRecord['currentVersion'] = currentRecordData['currentRecordVersion']['reference'];
+ currentRecord['versions'][currentRecordData['currentRecordVersion']['reference']] = {
+ 'data': currentRecordData['currentRecordVersion']['data'],
+ 'version': currentRecordData['currentRecordVersion']['version'],
+ 'previousVersion': currentRecordData['currentRecordVersion']['previousVersion'],
+ 'previousVersionKey': currentRecordData['currentRecordVersion']['previousVersionKey'],
+ 'creationDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date()),
+ 'updateDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date()),
+ 'accessDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date())
+ }
+ }
+ }
+
+ c = someParameters['parameters']['records']['deleted'].length;
+ for (i=0; i<c; i++) {
+ var currentRecordReference;
+
+ currentRecordReference = someParameters['parameters']['records']['deleted'][i];
+ delete aConnection['userData']['records'][currentRecordReference];
+ }
+
+ aConnection['userData']['lock'] = Clipperz.PM.Crypto.randomKey();
+ result['lock'] = aConnection['userData']['lock'];
+ result['result'] = 'done';
+ } else {
+ throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
+ }
+
+ //=====================================================================
+ //
+ // U N H A N D L E D M e t h o d
+ //
+ //=====================================================================
+ } else {
+ Clipperz.logError("Clipperz.PM.Proxy.Test.message - unhandled message: " + someParameters.message);
+ }
+
+ result = {
+ result: result,
+ toll: this.getTollForRequestType('MESSAGE')
+ }
+
+// return MochiKit.Async.succeed(result);
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_logout': function(someParameters) {
+// return MochiKit.Async.succeed({result: 'done'});
+ return {result: 'done'};
+ },
+
+ //=========================================================================
+ //#########################################################################
+/*
+ 'userDetails': function(aConnection) {
+ var result;
+
+ if (this.isTestData(aConnection)) {
+ var serializedHeader;
+ var version;
+
+//Clipperz.logDebug("### test data");
+ version = aConnection['userData']['userDetailsVersion'];
+ serializedHeader = Clipperz.Base.serializeJSON(aConnection['userData']['userDetails']);
+ result = Clipperz.PM.Crypto.encryptingFunctions.versions[version].encrypt(aConnection['userData']['__masterkey_test_value__'], serializedHeader);
+ } else {
+//Clipperz.logDebug("### NOT test data");
+ result = aConnection['userData']['userDetails'];
+ }
+
+ return result;
+ },
+
+ 'statistics': function(aConnection) {
+ var result;
+
+ if (aConnection['userData']['statistics'] != null) {
+ if (this.isTestData(aConnection)) {
+ var serializedStatistics;
+ var version;
+
+ version = aConnection['userData']['userDetailsVersion'];
+ serializedStatistics = Clipperz.Base.serializeJSON(aConnection['userData']['statistics']);
+ result = Clipperz.PM.Crypto.encryptingFunctions.versions[version].encrypt(aConnection['userData']['__masterkey_test_value__'], serializedStatistics);
+ } else {
+ result = aConnection['userData']['statistics'];
+ }
+ } else {
+ result = null;
+ }
+
+ return result;
+ },
+*/
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.MemoryDataStore.js b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.MemoryDataStore.js
new file mode 100644
index 0000000..ecc4408
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.MemoryDataStore.js
@@ -0,0 +1,643 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+try { if (typeof(Clipperz.PM.Proxy.Offline.DataStore) == 'undefined') { throw ""; }} catch (e) {
+ throw "Clipperz.PM.Proxy.Offline.MemoryDataStore depends on Clipperz.PM.Proxy.Offline.DataStore!";
+}
+
+//=============================================================================
+
+Clipperz.PM.Proxy.Offline.MemoryDataStore = function(args) {
+ args = args || {};
+
+ this._data = args.data || (typeof(_clipperz_dump_data_) != 'undefined' ? _clipperz_dump_data_ : null);
+ this._isReadOnly = (typeof(args.readOnly) == 'undefined' ? true : args.readOnly);
+ this._shouldPayTolls = args.shouldPayTolls || false;
+
+ this._tolls = {};
+ this._currentStaticConnection = null;
+
+ return this;
+}
+
+Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.MemoryDataStore, Clipperz.PM.Proxy.Offline.DataStore, {
+
+ //=========================================================================
+
+ 'resetData': function() {
+ this._data = {
+ 'users': {
+ 'catchAllUser': {
+ __masterkey_test_value__: 'masterkey',
+ s: '112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00',
+ v: '112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00'
+ }
+ }
+ };
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setupWithEncryptedData': function(someData) {
+ this._data = Clipperz.Base.deepClone(someData);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'setupWithData': function(someData) {
+ var deferredResult;
+ var resultData;
+ var i, c;
+
+//Clipperz.log(">>> Proxy.Test.setupWithData");
+ resultData = this._data;
+
+ deferredResult = new Clipperz.Async.Deferred("Proxy.Test.seupWithData", {trace:false});
+ c = someData['users'].length;
+
+ for (i=0; i<c; i++) {
+ var newConnection;
+ var recordConfiguration;
+
+ deferredResult.addMethod(this, 'userSerializedEncryptedData', someData['users'][i]);
+ deferredResult.addCallback(MochiKit.Base.bind(function(aUserSerializationContext) {
+ resultData['users'][aUserSerializationContext['credentials']['C']] = {
+ 's': aUserSerializationContext['credentials']['s'],
+ 'v': aUserSerializationContext['credentials']['v'],
+ 'version': aUserSerializationContext['data']['connectionVersion'],
+ 'userDetails': aUserSerializationContext['encryptedData']['user']['header'],
+ 'userDetailsVersion': aUserSerializationContext['encryptedData']['user']['version'],
+ 'statistics': aUserSerializationContext['encryptedData']['user']['statistics'],
+ 'lock': aUserSerializationContext['encryptedData']['user']['lock'],
+ 'records': this.rearrangeRecordsData(aUserSerializationContext['encryptedData']['records'])
+ }
+ }, this));
+ }
+
+ deferredResult.addCallback(MochiKit.Base.bind(function() {
+ this._data = resultData;
+ }, this));
+
+ deferredResult.callback();
+//Clipperz.log("<<< Proxy.Test.setupWithData");
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+
+ 'getTollForRequestType': function (aRequestType) {
+ var result;
+ var targetValue;
+ var cost;
+
+ targetValue = Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(32).toHexString().substring(2);
+ switch (aRequestType) {
+ case 'REGISTER':
+ cost = 5;
+ break;
+ case 'CONNECT':
+ cost = 5;
+ break;
+ case 'MESSAGE':
+ cost = 2;
+ break;
+ }
+
+ result = {
+ requestType: aRequestType,
+ targetValue: targetValue,
+ cost: cost
+ }
+
+ if (this.shouldPayTolls()) {
+ this.tolls()[targetValue] = result;
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'checkToll': function (aFunctionName, someParameters) {
+ if (this.shouldPayTolls()) {
+ var localToll;
+ var tollParameters;
+
+ tollParameters = someParameters['toll'];
+ localToll = this.tolls()[tollParameters['targetValue']];
+
+ if (localToll != null) {
+ if (! Clipperz.PM.Toll.validate(tollParameters['targetValue'], tollParameters['toll'], localToll['cost'])) {
+ throw "Toll value too low.";
+ };
+ } else {
+ throw "Missing toll";
+ }
+ }
+ },
+
+ //=========================================================================
+
+ 'currentStaticConnection': function () {
+ if (this._currentStaticConnection == null) {
+ this._currentStaticConnection = {};
+ }
+
+ return this._currentStaticConnection;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'getConnectionForRequest': function (aFunctionName, someParameters) {
+ var result;
+
+ if (this.shouldPayTolls()) {
+ if ((typeof(someParameters['toll']) != 'undefined') && (typeof(someParameters['toll']['targetValue']) != 'undefined')) {
+ result = this.tolls()[someParameters['toll']['targetValue']]['connection'];
+ if (typeof(result) == 'undefined') {
+ result = {};
+ }
+ } else {
+ result = {};
+ }
+ } else {
+ result = this.currentStaticConnection();
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'storeConnectionForRequestWithConnectionAndResponse': function (aFunctionName, someParameters, aConnection, aResponse) {
+ if (this.shouldPayTolls()) {
+ if ((typeof(aResponse['toll']) != 'undefined')
+ && (typeof(aResponse['toll']['targetValue']) != 'undefined')
+ && (typeof(this.tolls()[aResponse['toll']['targetValue']]) != 'undefined')
+ ) {
+ this.tolls()[aResponse['toll']['targetValue']]['connection'] = aConnection;
+ }
+ }
+ },
+
+ //=========================================================================
+
+ 'processMessage': function (aFunctionName, someParameters) {
+ var result;
+ var connection;
+
+ connection = this.getConnectionForRequest(aFunctionName, someParameters);
+
+ switch(aFunctionName) {
+ case 'knock':
+ result = this._knock(connection, someParameters);
+ break;
+ case 'registration':
+ this.checkToll(aFunctionName, someParameters);
+ result = this._registration(connection, someParameters.parameters);
+ break;
+ case 'handshake':
+ this.checkToll(aFunctionName, someParameters);
+ result = this._handshake(connection, someParameters.parameters);
+ break;
+ case 'message':
+ this.checkToll(aFunctionName, someParameters);
+ result = this._message(connection, someParameters.parameters);
+ break;
+ case 'logout':
+ this._currentStaticConnection = null;
+ result = this._logout(connection, someParameters.parameters);
+ break;
+ }
+
+ this.storeConnectionForRequestWithConnectionAndResponse(aFunctionName, someParameters, connection, result);
+
+ return MochiKit.Async.succeed(result);
+ },
+
+ //=========================================================================
+
+ '_knock': function(aConnection, someParameters) {
+ var result;
+
+ result = {
+ toll: this.getTollForRequestType(someParameters['requestType'])
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_registration': function(aConnection, someParameters) {
+ if (this.isReadOnly() == false) {
+ if (typeof(this.data()['users'][someParameters['credentials']['C']]) == 'undefined') {
+ this.data()['users'][someParameters['credentials']['C']] = {
+ 's': someParameters['credentials']['s'],
+ 'v': someParameters['credentials']['v'],
+ 'version': someParameters['credentials']['version'],
+// 'lock': Clipperz.Crypto.Base.generateRandomSeed(),
+ 'userDetails': someParameters['user']['header'],
+ 'statistics': someParameters['user']['statistics'],
+ 'userDetailsVersion': someParameters['user']['version'],
+ 'records': {}
+ }
+ } else {
+ throw "user already exists";
+ }
+ } else {
+ throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
+ }
+
+ result = {
+ result: {
+ 'lock': this.data()['users'][someParameters['credentials']['C']]['lock'],
+ 'result': 'done'
+ },
+ toll: this.getTollForRequestType('CONNECT')
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_handshake': function(aConnection, someParameters) {
+ var result;
+ var nextTollRequestType;
+
+ result = {};
+ if (someParameters.message == "connect") {
+ var userData;
+ var randomBytes;
+ var v;
+
+ userData = this.data()['users'][someParameters.parameters.C];
+
+ if ((typeof(userData) != 'undefined') && (userData['version'] == someParameters.version)) {
+ aConnection['userData'] = userData;
+ aConnection['C'] = someParameters.parameters.C;
+ } else {
+ aConnection['userData'] = this.data()['users']['catchAllUser'];
+ }
+
+ randomBytes = Clipperz.Crypto.Base.generateRandomSeed();
+ aConnection['b'] = new Clipperz.Crypto.BigInt(randomBytes, 16);
+ v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16);
+ aConnection['B'] = v.add(Clipperz.Crypto.SRP.g().powerModule(aConnection['b'], Clipperz.Crypto.SRP.n()));
+
+ aConnection['A'] = someParameters.parameters.A;
+
+ result['s'] = aConnection['userData']['s'];
+ result['B'] = aConnection['B'].asString(16);
+
+ nextTollRequestType = 'CONNECT';
+ } else if (someParameters.message == "credentialCheck") {
+ var v, u, S, A, K, M1;
+
+ v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16);
+ u = new Clipperz.Crypto.BigInt(Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(aConnection['B'].asString(10))).toHexString(), 16);
+ A = new Clipperz.Crypto.BigInt(aConnection['A'], 16);
+ S = (A.multiply(v.powerModule(u, Clipperz.Crypto.SRP.n()))).powerModule(aConnection['b'], Clipperz.Crypto.SRP.n());
+
+ K = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(S.asString(10))).toHexString().slice(2);
+
+ M1 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + aConnection['B'].asString(10) + K)).toHexString().slice(2);
+ if (someParameters.parameters.M1 == M1) {
+ var M2;
+
+ M2 = Clipperz.PM.Crypto.encryptingFunctions.versions[someParameters.version].hash(new Clipperz.ByteArray(A.asString(10) + someParameters.parameters.M1 + K)).toHexString().slice(2);
+ result['M2'] = M2;
+ } else {
+ throw new Error("Client checksum verification failed! Expected <" + M1 + ">, received <" + someParameters.parameters.M1 + ">.", "Error");
+ }
+
+ nextTollRequestType = 'MESSAGE';
+ } else if (someParameters.message == "oneTimePassword") {
+ var otpData;
+
+ otpData = this.data()['onetimePasswords'][someParameters.parameters.oneTimePasswordKey];
+
+ try {
+ if (typeof(otpData) != 'undefined') {
+ if (otpData['status'] == 'ACTIVE') {
+ if (otpData['key_checksum'] == someParameters.parameters.oneTimePasswordKeyChecksum) {
+ result = {
+ 'data': otpData['data'],
+ 'version': otpData['version']
+ }
+
+ otpData['status'] = 'REQUESTED';
+ } else {
+ otpData['status'] = 'DISABLED';
+ throw "The requested One Time Password has been disabled, due to a wrong keyChecksum";
+ }
+ } else {
+ throw "The requested One Time Password was not active";
+ }
+ } else {
+ throw "The requested One Time Password has not been found"
+ }
+ } catch (exception) {
+ result = {
+ 'data': Clipperz.PM.Crypto.randomKey(),
+ 'version': Clipperz.PM.Connection.communicationProtocol.currentVersion
+ }
+ }
+ nextTollRequestType = 'CONNECT';
+ } else {
+ Clipperz.logError("Clipperz.PM.Proxy.Test.handshake - unhandled message: " + someParameters.message);
+ }
+
+ result = {
+ result: result,
+ toll: this.getTollForRequestType(nextTollRequestType)
+ }
+
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_message': function(aConnection, someParameters) {
+ var result;
+
+ result = {};
+
+ //=====================================================================
+ //
+ // R E A D - O N L Y M e t h o d s
+ //
+ //=====================================================================
+ if (someParameters.message == 'getUserDetails') {
+ var recordsStats;
+ var recordReference;
+
+ recordsStats = {};
+ for (recordReference in aConnection['userData']['records']) {
+ recordsStats[recordReference] = {
+ 'updateDate': aConnection['userData']['records'][recordReference]['updateDate']
+ }
+ }
+
+ result['header'] = this.userDetails(aConnection);
+ result['statistics'] = this.statistics(aConnection);
+ result['maxNumberOfRecords'] = aConnection['userData']['maxNumberOfRecords'];
+ result['version'] = aConnection['userData']['userDetailsVersion'];
+ result['recordsStats'] = recordsStats;
+
+ if (this.isReadOnly() == false) {
+ var lock;
+
+ if (typeof(aConnection['userData']['lock']) == 'undefined') {
+ aConnection['userData']['lock'] = "<<LOCK>>";
+ }
+
+ result['lock'] = aConnection['userData']['lock'];
+ }
+
+ //=====================================================================
+ } else if (someParameters.message == 'getRecordDetail') {
+/*
+ var recordData;
+ var currentVersionData;
+
+ recordData = this.userData()['records'][someParameters['parameters']['reference']];
+ result['reference'] = someParameters['parameters']['reference'];
+ result['data'] = recordData['data'];
+ result['version'] = recordData['version'];
+ result['creationData'] = recordData['creationDate'];
+ result['updateDate'] = recordData['updateDate'];
+ result['accessDate'] = recordData['accessDate'];
+
+ currentVersionData = recordData['versions'][recordData['currentVersion']];
+
+ result['currentVersion'] = {};
+ result['currentVersion']['reference'] = recordData['currentVersion'];
+ result['currentVersion']['version'] = currentVersionData['version'];
+ result['currentVersion']['header'] = currentVersionData['header'];
+ result['currentVersion']['data'] = currentVersionData['data'];
+ result['currentVersion']['creationData'] = currentVersionData['creationDate'];
+ result['currentVersion']['updateDate'] = currentVersionData['updateDate'];
+ result['currentVersion']['accessDate'] = currentVersionData['accessDate'];
+ if (typeof(currentVersionData['previousVersion']) != 'undefined') {
+ result['currentVersion']['previousVersionKey'] = currentVersionData['previousVersionKey'];
+ result['currentVersion']['previousVersion'] = currentVersionData['previousVersion'];
+ }
+*/
+ MochiKit.Base.update(result, aConnection['userData']['records'][someParameters['parameters']['reference']]);
+ result['reference'] = someParameters['parameters']['reference'];
+
+ //=====================================================================
+ //
+ // R E A D - W R I T E M e t h o d s
+ //
+ //=====================================================================
+ } else if (someParameters.message == 'upgradeUserCredentials') {
+ if (this.isReadOnly() == false) {
+ var parameters;
+ var credentials;
+
+ parameters = someParameters['parameters'];
+ credentials = parameters['credentials'];
+
+ if ((credentials['C'] == null)
+ || (credentials['s'] == null)
+ || (credentials['v'] == null)
+ || (credentials['version'] != Clipperz.PM.Connection.communicationProtocol.currentVersion)
+ ) {
+ result = Clipperz.PM.DataModel.User.exception.CredentialUpgradeFailed;
+ } else {
+ var oldCValue;
+ oldCValue = aConnection['C'];
+
+ this.data()['users'][credentials['C']] = aConnection['userData'];
+ aConnection['C'] = credentials['C'];
+
+ aConnection['userData']['s'] = credentials['s'];
+ aConnection['userData']['v'] = credentials['v'];
+ aConnection['userData']['version'] = credentials['version'];
+
+ aConnection['userData']['userDetails'] = parameters['user']['header'];
+ aConnection['userData']['userDetailsVersion'] = parameters['user']['version'];
+ aConnection['userData']['statistics'] = parameters['user']['statistics'];
+
+ aConnection['userData']['lock'] = parameters['user']['lock'];
+
+ delete this.data()['users'][oldCValue];
+
+ result = {result:"done", parameters:parameters};
+ }
+ } else {
+ throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
+ }
+
+ //=====================================================================
+
+ } else if (someParameters.message == 'saveChanges') {
+ if (this.isReadOnly() == false) {
+ var i, c;
+
+ if (aConnection['userData']['lock'] != someParameters['parameters']['user']['lock']) {
+ throw "the lock attribute is not processed correctly"
+ }
+
+ aConnection['userData']['userDetails'] = someParameters['parameters']['user']['header'];
+ aConnection['userData']['statistics'] = someParameters['parameters']['user']['statistics'];
+ aConnection['userData']['userDetailsVersion'] = someParameters['parameters']['user']['version'];
+
+ c = someParameters['parameters']['records']['updated'].length;
+ for (i=0; i<c; i++) {
+ var currentRecord;
+ var currentRecordData;
+
+ currentRecordData = someParameters['parameters']['records']['updated'][i];
+ currentRecord = aConnection['userData']['records'][currentRecordData['record']['reference']];
+
+ if (
+ (typeof(aConnection['userData']['records'][currentRecordData['record']['reference']]) == 'undefined')
+ &&
+ (typeof(currentRecordData['currentRecordVersion']) == 'undefined')
+ ) {
+ throw "Record added without a recordVersion";
+ }
+
+ if (currentRecord == null) {
+ currentRecord = {};
+ currentRecord['versions'] = {};
+ currentRecord['creationDate'] = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
+ currentRecord['accessDate'] = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
+
+ aConnection['userData']['records'][currentRecordData['record']['reference']] = currentRecord;
+ }
+
+ currentRecord['data'] = currentRecordData['record']['data'];
+ currentRecord['version'] = currentRecordData['record']['version'];
+ currentRecord['updateDate'] = Clipperz.PM.Date.formatDateWithUTCFormat(new Date());
+
+ if (typeof(currentRecordData['currentRecordVersion']) != 'undefined') {
+ currentRecord['currentVersion'] = currentRecordData['currentRecordVersion']['reference'];
+ currentRecord['versions'][currentRecordData['currentRecordVersion']['reference']] = {
+ 'data': currentRecordData['currentRecordVersion']['data'],
+ 'version': currentRecordData['currentRecordVersion']['version'],
+ 'previousVersion': currentRecordData['currentRecordVersion']['previousVersion'],
+ 'previousVersionKey': currentRecordData['currentRecordVersion']['previousVersionKey'],
+ 'creationDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date()),
+ 'updateDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date()),
+ 'accessDate': Clipperz.PM.Date.formatDateWithUTCFormat(new Date())
+ }
+ }
+ }
+
+ c = someParameters['parameters']['records']['deleted'].length;
+ for (i=0; i<c; i++) {
+ var currentRecordReference;
+
+ currentRecordReference = someParameters['parameters']['records']['deleted'][i];
+ delete aConnection['userData']['records'][currentRecordReference];
+ }
+
+ aConnection['userData']['lock'] = Clipperz.PM.Crypto.randomKey();
+ result['lock'] = aConnection['userData']['lock'];
+ result['result'] = 'done';
+ } else {
+ throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly;
+ }
+
+ //=====================================================================
+ //
+ // U N H A N D L E D M e t h o d
+ //
+ //=====================================================================
+ } else {
+ Clipperz.logError("Clipperz.PM.Proxy.Test.message - unhandled message: " + someParameters.message);
+ }
+
+ result = {
+ result: result,
+ toll: this.getTollForRequestType('MESSAGE')
+ }
+
+// return MochiKit.Async.succeed(result);
+ return result;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_logout': function(someParameters) {
+// return MochiKit.Async.succeed({result: 'done'});
+ return {result: 'done'};
+ },
+
+ //=========================================================================
+ //#########################################################################
+
+ 'isTestData': function(aConnection) {
+ return (typeof(aConnection['userData']['__masterkey_test_value__']) != 'undefined');
+ },
+
+ 'userDetails': function(aConnection) {
+ var result;
+
+ if (this.isTestData(aConnection)) {
+ var serializedHeader;
+ var version;
+
+//Clipperz.logDebug("### test data");
+ version = aConnection['userData']['userDetailsVersion'];
+ serializedHeader = Clipperz.Base.serializeJSON(aConnection['userData']['userDetails']);
+ result = Clipperz.PM.Crypto.encryptingFunctions.versions[version].encrypt(aConnection['userData']['__masterkey_test_value__'], serializedHeader);
+ } else {
+//Clipperz.logDebug("### NOT test data");
+ result = aConnection['userData']['userDetails'];
+ }
+
+ return result;
+ },
+
+ 'statistics': function(aConnection) {
+ var result;
+
+ if (aConnection['userData']['statistics'] != null) {
+ if (this.isTestData(aConnection)) {
+ var serializedStatistics;
+ var version;
+
+ version = aConnection['userData']['userDetailsVersion'];
+ serializedStatistics = Clipperz.Base.serializeJSON(aConnection['userData']['statistics']);
+ result = Clipperz.PM.Crypto.encryptingFunctions.versions[version].encrypt(aConnection['userData']['__masterkey_test_value__'], serializedStatistics);
+ } else {
+ result = aConnection['userData']['statistics'];
+ }
+ } else {
+ result = null;
+ }
+
+ return result;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.js b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.js
new file mode 100644
index 0000000..6d6ee1e
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.js
@@ -0,0 +1,72 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+
+//=============================================================================
+
+Clipperz.PM.Proxy.Offline = function(args) {
+ args = args || {};
+
+ Clipperz.PM.Proxy.Offline.superclass.constructor.call(this, args);
+
+ this._dataStore = args.dataStore || new Clipperz.PM.Proxy.Offline.DataStore(args);
+
+ return this;
+}
+
+Clipperz.Base.extend(Clipperz.PM.Proxy.Offline, Clipperz.PM.Proxy, {
+
+ 'toString': function () {
+ return "Clipperz.PM.Proxy.Offline";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'dataStore': function () {
+ return this._dataStore;
+ },
+
+ //-------------------------------------------------------------------------
+
+ '_sendMessage': function(aFunctionName, aVersion, someParameters) {
+ return this.dataStore().processMessage(aFunctionName, someParameters);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'isReadOnly': function () {
+ return this.dataStore().isReadOnly();
+ },
+
+ 'canRegisterNewUsers': function () {
+ return this.dataStore().canRegisterNewUsers();
+ },
+
+ //-------------------------------------------------------------------------
+
+ __syntaxFix__: "syntax fix"
+
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Test.js b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Test.js
new file mode 100644
index 0000000..83d9244
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Test.js
@@ -0,0 +1,161 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.Proxy) == 'undefined') { Clipperz.PM.Proxy = {}; }
+
+//=============================================================================
+
+Clipperz.PM.Proxy.Test = function(args) {
+ Clipperz.PM.Proxy.Test.superclass.constructor.call(this, args);
+
+ args = args || {};
+
+ this._expectedRequests = (args.shouldCheckExpectedRequests === true) ? [] : null;
+ this._isExpectingRequests = true;
+ this._unexpectedRequests = [];
+
+ this.dataStore().resetData();
+
+ return this;
+}
+
+Clipperz.Base.extend(Clipperz.PM.Proxy.Test, Clipperz.PM.Proxy.Offline, {
+
+ 'toString': function() {
+ return "Clipperz.PM.Proxy.Test";
+ },
+
+ //=========================================================================
+
+ 'expectedRequests': function () {
+ return this._expectedRequests;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'shouldCheckExpectedRequests': function () {
+ return (this._expectedRequests != null);
+ },
+
+ 'setShouldCheckExpectedRequests': function(aValue) {
+ if (aValue) {
+ this._expectedRequests = aValue;
+ } else {
+ this._expectedRequests = null;
+ }
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'shouldNotReceiveAnyFurtherRequest': function () {
+ this._isExpectingRequests = false;
+ },
+
+ 'mayReceiveMoreRequests': function () {
+ this._isExpectingRequests = true;
+ this.resetUnexpectedRequests();
+ },
+
+ 'isExpectingRequests': function () {
+ return this._isExpectingRequests;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'unexpectedRequests': function () {
+ return this._unexpectedRequests;
+ },
+
+ 'resetUnexpectedRequests': function () {
+ this._unexpectedRequests = [];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'testExpectedRequestParameters': function (aPath, anActualRequest, anExpectedRequest) {
+ var aKey;
+ for (aKey in anExpectedRequest) {
+ if (typeof(anActualRequest[aKey]) == 'undefined') {
+ throw "the expected paramter [" + aKey + "] is missing from the actual request";
+ }
+ if (typeof(anExpectedRequest[aKey]) == 'object') {
+ this.testExpectedRequestParameters(aPath + "." + aKey, anActualRequest[aKey], anExpectedRequest[aKey])
+ } else {
+ if (! anExpectedRequest[aKey](anActualRequest[aKey])) {
+ throw "wrong value for paramter [" + aKey + "]; got '" + anActualRequest[aKey] + "'";
+ }
+ }
+ }
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'checkRequest': function(aFunctionName, someParameters) {
+ if (this.shouldCheckExpectedRequests()) {
+ var expectedRequest;
+
+ expectedRequest = this.expectedRequests().pop();
+ if (expectedRequest == null) {
+ throw "Proxy.Test.sentMessage: no expected result specified. Got request '" + aFunctionName + "': " + someParameters;
+ }
+
+ try {
+ if (aFunctionName != expectedRequest.functionName) {
+ throw "wrong function name. Got '" + aFunctionName + "', expected '" + expectedRequest.request.functionName + "'";
+ }
+
+ this.testExpectedRequestParameters("parameters", someParameters, expectedRequest.parameters);
+ } catch(exception) {
+ throw "Proxy.Test.sentMessage[" + expectedRequest.name + "]: " + exception;
+ }
+ }
+ },
+
+ //=========================================================================
+
+ '_sendMessage': function(aFunctionName, aVersion, someParameters) {
+ var result;
+
+ if (this.isExpectingRequests() == false) {
+// throw Clipperz.PM.Connection.exception.UnexpectedRequest;
+Clipperz.log("UNEXPECTED REQUEST " + aFunctionName /* + ": " + Clipperz.Base.serializeJSON(someParameters) */);
+ this.unexpectedRequests().push({'functionName':aFunctionName, 'someParameters': someParameters});
+ };
+//if (aFunctionName == 'knock') {
+// console.log(">>> send message - " + aFunctionName, someParameters);
+//} else {
+// console.log(">>> SEND MESSAGE - " + aFunctionName + " [" + someParameters['parameters']['message'] + "]", someParameters['parameters']['parameters']);
+//}
+ this.checkRequest(aFunctionName, someParameters);
+ result = Clipperz.PM.Proxy.Test.superclass._sendMessage.call(this, aFunctionName, aVersion, someParameters);
+
+ return result;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+
+});
+
diff --git a/frontend/delta/js/Clipperz/PM/Strings.js b/frontend/delta/js/Clipperz/PM/Strings.js
new file mode 100644
index 0000000..7e855ff
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Strings.js
@@ -0,0 +1,285 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.Strings) == 'undefined') { Clipperz.PM.Strings = {}; }
+if (typeof(Clipperz.PM.Strings.Languages) == 'undefined') { Clipperz.PM.Strings.Languages = {}; }
+
+//-----------------------------------------------------------------------------
+/*
+Clipperz.PM.Strings.standardStrings = {
+ 'loginPanelSwitchLanguageSelectOptions': [
+/ *
+ {tag:'option', html:"Arabic (Oman) (العربية)", value:'ar-OM', disabled:true},
+ {tag:'option', html:"Arabic (Syria) (العربية)", value:'ar-SY', disabled:true},
+ {tag:'option', html:"Bahasa Indonesia", value:'id-ID', disabled:true},
+ {tag:'option', html:"Bulgarian (Български)", value:'bg-BG', disabled:true},
+ {tag:'option', html:"Català", value:'ca-ES', disabled:true},
+ {tag:'option', html:"Chinese (Simplified) (简体中文)", value:'zh-CN', disabled:true},
+ {tag:'option', html:"Chinese (Traditional) (正體中文)", value:'zh-TW', disabled:true},
+ {tag:'option', html:"Czech (Česky)", value:'cs-CZ', disabled:true},
+ {tag:'option', html:"Dansk", value:'da-DK', disabled:true},
+ {tag:'option', html:"Deutsch", value:'de-DE'/ *, disabled:true* /},
+ {tag:'option', html:"English (American)", value:'en-US'/ *, disabled:true* /},
+ {tag:'option', html:"English (British)", value:'en-GB'/ *, disabled:true* /},
+ {tag:'option', html:"English (Canadian)", value:'en-CA'/ *, disabled:true* /},
+ {tag:'option', html:"Español", value:'es-ES', disabled:true},
+ {tag:'option', html:"Eesti", value:'et-EE', disabled:true},
+ {tag:'option', html:"Français", value:'fr-FR', disabled:true},
+ {tag:'option', html:"Galego", value:'gl-ES', disabled:true},
+ {tag:'option', html:"Greek (Ελληνικά)", value:'el-GR', disabled:true},
+ {tag:'option', html:"Íslenska", value:'is-IS', disabled:true},
+ {tag:'option', html:"Italiano", value:'it-IT'/ *, disabled:true* /},
+ {tag:'option', html:"Japanese (日本語)", value:'ja-JP', disabled:true},
+ {tag:'option', html:"Korean (한국어)", value:'ko-KR', disabled:true},
+ {tag:'option', html:"Latviešu", value:'lv-LV', disabled:true},
+ {tag:'option', html:"Lietuvių", value:'lt-LT', disabled:true},
+ {tag:'option', html:"Macedonian (Македонски)", value:'mk-MK', disabled:true},
+ {tag:'option', html:"Magyar", value:'hu-HU', disabled:true},
+ {tag:'option', html:"Nederlands", value:'nl-NL', disabled:true},
+ {tag:'option', html:"Norsk bokmål", value:'nb-NO', disabled:true},
+ {tag:'option', html:"Norsk nynorsk", value:'nn-NO', disabled:true},
+ {tag:'option', html:"Persian (Western) (فارسى)", value:'fa-IR', disabled:true},
+ {tag:'option', html:"Polski", value:'pl-PL', disabled:true},
+ {tag:'option', html:"Português", value:'pt-PT'/ *, disabled:true* /},
+ {tag:'option', html:"Português Brasileiro", value:'pt-BR'/ *, disabled:true* /},
+ {tag:'option', html:"Românä", value:'ro-RO', disabled:true},
+ {tag:'option', html:"Russian (Русский)", value:'ru-RU', disabled:true},
+ {tag:'option', html:"Slovak (Slovenčina)", value:'sk-SK', disabled:true},
+ {tag:'option', html:"Slovenian (Slovenščina)", value:'sl-SI', disabled:true},
+ {tag:'option', html:"Suomi", value:'fi-FI', disabled:true},
+ {tag:'option', html:"Svenska", value:'sv-SE', disabled:true},
+ {tag:'option', html:"Thai (ไทย)", value:'th-TH', disabled:true},
+ {tag:'option', html:"Türkçe", value:'tr-TR', disabled:true},
+ {tag:'option', html:"Ukrainian (Українська)", value:'uk-UA', disabled:true}
+* /
+ {tag:'option', html:"Arabic (العربية)", value:"ar", disabled:true, cls:'disabledOption'},
+// {tag:'option', html:"Chinese (中文)", value:"zh", disabled:true},
+ {tag:'option', html:"Chinese (Simplified) (简体中文)", value:'zh-CN'},
+ {tag:'option', html:"Dutch (Nederlands)", value:"nl-NL", disabled:true, cls:'disabledOption'},
+ {tag:'option', html:"English", value:"en-US"},
+ {tag:'option', html:"French (Français)", value:"fr-FR"},
+ {tag:'option', html:"German (Deutsch)", value:"de-DE", disabled:true, cls:'disabledOption'},
+ {tag:'option', html:"Greek (Ελληνικά)", value:"el-GR", disabled:true, cls:'disabledOption'},
+ {tag:'option', html:"Hebrew (עברית)", value:"he-IL", disabled:true, cls:'disabledOption'},
+ {tag:'option', html:"Italian (Italiano)", value:"it-IT"},
+ {tag:'option', html:"Japanese (日本語)", value:"ja-JP"},
+ {tag:'option', html:"Korean (한국어)", value:"ko-KR", disabled:true, cls:'disabledOption'},
+ {tag:'option', html:"Norwegian (Norsk)", value:"no", disabled:true, cls:'disabledOption'},
+ {tag:'option', html:"Persian (فارسی)", value:"fa-IR", disabled:true, cls:'disabledOption'},
+ {tag:'option', html:"Polish (Polski)", value:"pl-PL", disabled:true, cls:'disabledOption'},
+ {tag:'option', html:"Portuguese (Português)", value:"pt-BR"},
+ {tag:'option', html:"Russian (Русский)", value:"ru-RU", disabled:true, cls:'disabledOption'},
+ {tag:'option', html:"Spanish (Español)", value:"es-ES"},
+ {tag:'option', html:"Swedish (Svenska)", value:"sv-SE", disabled:true, cls:'disabledOption'},
+ {tag:'option', html:"Turkish (Türkçe)", value:"tr-TR", disabled:true, cls:'disabledOption'},
+ {tag:'option', html:"Vietnamese (Tiếng Việt)", value:"vi-VN", disabled:true, cls:'disabledOption'}
+ ]
+}
+*/
+
+Clipperz.PM.Strings.GeneralSettings = {
+ 'defaults': {
+// 'loginFormAarghThatsBadUrl': "http://www.clipperz.com/support/faq/account_faq",
+// 'loginFormVerifyTheCodeUrl': "http://www.clipperz.com/learn_more/reviewing_the_code",
+
+// 'donateHeaderLinkUrl': "http://www.clipperz.com/donations",
+// 'creditsHeaderLinkUrl': "http://www.clipperz.com/credits",
+// 'feedbackHeaderLinkUrl': "http://www.clipperz.com/contact",
+// 'helpHeaderLinkUrl': "http://www.clipperz.com/support/user_guide",
+// 'forumHeaderLinkUrl': "http://www.clipperz.com/forum",
+
+// 'httpAuthBookmarkletConfiguration': {tag:'textarea', id:'httpAuthDefaultConfiguration', html:"" +
+// "{ \"page\":{\"title\":\"HTTP authentication\"}," + "\n" +
+// " \"form\":{\"attributes\": {" + "\n" +
+// " \"action\":\"\"," + "\n" +
+// " \"type\":\"http_auth\"" + "\n" +
+// " }, \"inputs\": [" + "\n" +
+// " {\"type\":\"text\",\"name\":\"url\",\"value\":\"\"}," + "\n" +
+// " {\"type\":\"text\",\"name\":\"username\",\"value\":\"\"}," + "\n" +
+// " {\"type\":\"password\",\"name\":\"password\",\"value\":\"\"}" + "\n" +
+// " ]}, \"version\":\"0.2.3\"}"
+// },
+
+ 'directLoginJumpPageUrl': "",
+ 'defaultFaviconUrl': "data:application/octet-stream;charset=utf-8;base64,AAABAAEAFxcAAAEAGAD8BgAAFgAAACgAAAAXAAAALgAAAAEAGAAAAAAAAAAAABIXAAASFwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////9zAC////////////////////////////////////////////////////////////////////////////////////////////9pAG////////////////////////////////////////////////////////////////////////////////////////////9rAC////////////////////////////////////////////////////////////////////////////////////////////9yAHP////////////////////////IyMizs7O6urrq6ur////////////Ozs6zs7Ozs7Pq6ur///////////////////////8AAAD////////////////////V1dWXl5eXl5eXl5elpaX4+Pj////Ozs6Xl5eXl5eXl5eenp7///////////////////////8AAAD////////////////////Ozs6Xl5eXl5eXl5eXl5fBwcHq6uqenp6Xl5eXl5eXl5eXl5f///////////////////////8AAAD////////////////////j4+OXl5eXl5eXl5eXl5eXl5elpaWXl5eXl5eXl5eXl5ezs7P///////////////////////8AAAD////////////////////////IyMiXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eenp7x8fH////////////////////////////////////////////////////4+PilpaWXl5eXl5eXl5eXl5eXl5eXl5eXl5fOzs7////////////////////////////////////////////////////////q6uq6urqXl5eXl5eXl5eXl5eXl5eXl5eenp7V1dX4+Pj///////////////////////8AAAD////////////4+PjOzs6lpaWXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5e6urrj4+P///////////////8AAAD////////////BwcGXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5fx8fH///////////8AAAD///////////+zs7OXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5eXl5fj4+P///////////8AAAD////////////IyMiXl5eXl5eXl5eXl5e6urqXl5eXl5eXl5eXl5esrKylpaWXl5eXl5eXl5eenp7x8fH///////////8AAAD////////////////Ozs7Ozs7V1dX4+Pj///+Xl5eXl5eXl5eXl5fOzs7////q6urOzs7Ozs7q6ur///////////////8AAAD///////////////////////////////////+Xl5eXl5eXl5eXl5fOzs7///////////////////////////////////8AAAD///////////////////////////////////+Xl5eXl5eXl5eXl5fOzs7///////////////////////////////////8AAAD///////////////////////////////////+Xl5eXl5eXl5eXl5fOzs7///////////////////////////////////8AAAD////////////////////////////////////IyMiXl5eXl5eenp7x8fH///////////////////////////////////8AAAD////////////////////////////////////////j4+Pj4+Px8fH///////////////////////////////////////8AAAD///////////////////////////////////////////////////////////////////////////////////////////8AAAD///////////////////////////////////////////////////////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo=",
+ 'defaultFaviconUrl_IE': "https://www.clipperz.com/images/icons/misc/favicon.ico",
+
+// 'icons_baseUrl': "https://www.clipperz.com/images/icons",
+
+// 'passwordGeneratorLowercaseCharset': "abcdefghijklmnopqrstuvwxyz",
+// 'passwordGeneratorUppercaseCharset': "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+// 'passwordGeneratorNumberCharset': "0123456789",
+// 'passwordGeneratorSymbolCharset': "!@#$%^&*+?[]{}/|\\<>,.;:~=-_",
+
+// 'passwordGenerator': {
+// 'lowercaseCharset': "abcdefghijklmnopqrstuvwxyz",
+// 'uppercaseCharset': "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+// 'numberCharset': "0123456789",
+// 'symbolCharset': "!@#$%^&*+?[]{}/|\\<>,.;:~=-_",
+// },
+
+ '_': ""
+ }
+}
+
+Clipperz.PM.Strings.defaultLanguages = {
+ 'default': "en-us",
+
+// 'de': "de-de",
+// 'el': "el-gr",
+// 'he': "he-il",
+// 'ru': "ru-ru",
+
+ 'fr': "fr-fr",
+ 'es': "es-es",
+ 'zh': "zh-cn",
+ 'ja': "ja-jp",
+ 'pt': "pt-br",
+ 'it': "it-it",
+ 'en': "en-us"
+}
+
+Clipperz.PM.Strings.inputTypeToRecordFieldType = {
+ 'text': 'TXT',
+ 'password': 'PWD',
+ 'checkbox': 'CHECK',
+ 'radio': 'RADIO',
+ 'select': 'SELECT'
+};
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.Strings.translateBookmarklet = function (aBookmarkletString) {
+ var result;
+
+ result = aBookmarkletString;
+
+ result = result.replace(/@BOOKMARKLET_NO_EXCEPTION_MESSAGE@/, Clipperz.PM.Strings.getValue('bookmarkletCopy.noExceptionMessage'));
+ result = result.replace(/@BOOKMARKLET_EXCEPTION_MESSAGE@/, Clipperz.PM.Strings.getValue('bookmarkletCopy.exceptionMessage'));
+ result = result.replace(/@BOOKMARKLET_COPY@/, Clipperz.PM.Strings.getValue('bookmarkletCopy.copy'));
+ result = result.replace(/@BOOKMARKLET_SUCCESSFUL_MESSAGE@/, Clipperz.PM.Strings.getValue('bookmarkletCopy.successfulMessage'));
+ result = result.replace(/@BOOKMARKLET_FAIL_MESSAGE@/, Clipperz.PM.Strings.getValue('bookmarkletCopy.failMessage'));
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.Strings.Languages.setSelectedLanguage = function(aLanguage) {
+ var language;
+ var selectedLanguage;
+
+ language = (aLanguage || Clipperz.PM.Strings.preferredLanguage || 'default').toLowerCase();
+ if (typeof(Clipperz.PM.Strings.defaultLanguages[language]) != 'undefined') {
+ language = Clipperz.PM.Strings.defaultLanguages[language];
+ }
+
+ if (typeof(Clipperz.PM.Strings.Languages[language]) != 'undefined') {
+ selectedLanguage = language;
+ } else if (typeof(Clipperz.PM.Strings.defaultLanguages[language.substr(0,2)]) != 'undefined') {
+ selectedLanguage = Clipperz.PM.Strings.defaultLanguages[language.substr(0,2)];
+ } else {
+ selectedLanguage = Clipperz.PM.Strings.defaultLanguages['default'];
+ }
+
+ if (selectedLanguage != Clipperz.PM.Strings.selectedLanguage) {
+ var translations;
+
+ Clipperz.PM.Strings.selectedLanguage = selectedLanguage;
+
+ translations = {};
+// MochiKit.Base.update(translations, Clipperz.PM.Strings.standardStrings)
+
+ MochiKit.Base.updatetree(translations, Clipperz.PM.Strings.Languages['defaults']);
+ MochiKit.Base.updatetree(translations, Clipperz.PM.Strings.GeneralSettings['defaults']);
+
+ MochiKit.Base.updatetree(translations, Clipperz.PM.Strings.Languages[Clipperz.PM.Strings.defaultLanguages['default']]);
+ MochiKit.Base.updatetree(translations, Clipperz.PM.Strings.GeneralSettings[Clipperz.PM.Strings.defaultLanguages['default']]);
+
+ MochiKit.Base.updatetree(translations, Clipperz.PM.Strings.Languages[selectedLanguage]);
+ MochiKit.Base.updatetree(translations, Clipperz.PM.Strings.GeneralSettings[selectedLanguage]);
+
+ Clipperz.PM.Strings.stringsObjectStore = new Clipperz.KeyValueObjectStore(/*{'name':'String.stringsObjectStore [1]'}*/);
+ Clipperz.PM.Strings.stringsObjectStore.initWithValues(translations);
+
+ if (typeof(bookmarklet) != 'undefined') {
+ Clipperz.PM.Strings.stringsObjectStore.setValue('bookmarklet', Clipperz.PM.Strings.translateBookmarklet(bookmarklet));
+ }
+
+ MochiKit.Signal.signal(Clipperz.PM.Strings.Languages, 'switchLanguage', selectedLanguage);
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.Strings.getValue = function (aKeyPath, someKeyValues) {
+ var result;
+
+ result = Clipperz.PM.Strings.stringsObjectStore.getValue(aKeyPath);
+
+ if (typeof(result) == 'string') {
+ if (typeof (someKeyValues) != 'undefined') {
+ var key;
+
+ for (key in someKeyValues) {
+ result = result.replace( new RegExp(key), someKeyValues[key]);
+ }
+ }
+
+ result = result.replace(new RegExp('\n'), '<br>');
+ }
+
+ return result;
+}
+
+Clipperz.PM.Strings.errorDescriptionForException = function (anException) {
+ var result;
+
+ result = Clipperz.PM.Strings.getValue('exceptionsMessages' + '.' + anException.name);
+
+ if (result == null) {
+ result = anException.message;
+ }
+
+ return result;
+},
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.Strings.Languages.initSetup = function() {
+ var language;
+ var languageParser;
+
+ language = navigator.language || navigator.userLanguage; // en, en-US, .... "de", "nb-no"
+ languageParser = new RegExp("language=([a-z]{2}(?:\-[a-z]{2})?)(\&|$)", "i");
+ if (languageParser.test(window.location.search)) {
+ language = RegExp.$1;
+ }
+
+ Clipperz.PM.Strings.preferredLanguage = language.toLowerCase();
+ Clipperz.PM.Strings.Languages.setSelectedLanguage(Clipperz.PM.Strings.preferredLanguage);
+}
+
+//-----------------------------------------------------------------------------
diff --git a/frontend/delta/js/Clipperz/PM/Strings/MessagePanelConfigurations.js b/frontend/delta/js/Clipperz/PM/Strings/MessagePanelConfigurations.js
new file mode 100644
index 0000000..7565d2d
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Strings/MessagePanelConfigurations.js
@@ -0,0 +1,384 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.Strings) == 'undefined') { Clipperz.PM.Strings = {}; }
+
+Clipperz.PM.Strings.messagePanelConfigurations = {
+
+
+ //-------------------------------------------------------------------------
+ //
+ // Registration - connection
+ //
+ 'registration_verify': function() {
+ return {
+ 'title': null,
+ 'text': Clipperz.PM.Strings['connectionRegistrationSendingRequestMessageText']
+ }
+ },
+
+ 'registration_sendingCredentials': function() {
+ return {
+ 'title': null,
+ 'text': Clipperz.PM.Strings['connectionRegistrationSendingCredentialsMessageText']
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ //
+ // One Time Password login message panel
+ //
+
+ 'OTP_login_start': function() {
+ return {
+ 'title': Clipperz.PM.Strings['OTPloginMessagePanelInitialTitle'],
+ 'text': Clipperz.PM.Strings['OTPloginMessagePanelInitialText'],
+ 'steps': '+3',
+ 'buttons': {}
+ }
+ },
+
+ 'OTP_login_loadingOTP': function() {
+ return {
+ 'title': Clipperz.PM.Strings['OTPloginMessagePanelLoadingTitle'],
+ 'text': Clipperz.PM.Strings['OTPloginMessagePanelLoadingText']
+ }
+ },
+
+ 'OTP_login_extractingPassphrase': function() {
+ return {
+ 'title': Clipperz.PM.Strings['OTPloginMessagePanelProcessingTitle'],
+ 'text': Clipperz.PM.Strings['OTPloginMessagePanelProcessingText']
+ }
+ },
+
+
+ //-------------------------------------------------------------------------
+ //
+ // Login message panel
+ //
+ 'login_start': function() {
+ return {
+ 'title': Clipperz.PM.Strings['loginMessagePanelInitialTitle'],
+ 'text': Clipperz.PM.Strings['loginMessagePanelInitialText'],
+ 'steps': '+7',
+ 'buttons': {
+ 'ok': Clipperz.PM.Strings['loginMessagePanelInitialButtonLabel']
+ }
+ }
+ },
+
+ 'login_connected': function() {
+ return {
+ 'title': Clipperz.PM.Strings['loginMessagePanelConnectedTitle'],
+ 'text': Clipperz.PM.Strings['loginMessagePanelConnectedText'],
+ 'buttons': {}
+ }
+ },
+
+ 'login_failed': function() {
+ return {
+ 'title': Clipperz.PM.Strings['loginMessagePanelFailureTitle'],
+ 'text': Clipperz.PM.Strings['loginMessagePanelFailureText'],
+ 'button': Clipperz.PM.Strings['loginMessagePanelFailureButtonLabel']
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ //
+ // Login message panel - connection
+ //
+ 'connection_sendingCredentials': function() {
+ return {
+ 'title': Clipperz.PM.Strings['connectionLoginSendingCredentialsMessageTitle'],
+ 'text': Clipperz.PM.Strings['connectionLoginSendingCredentialsMessageText']
+ }
+ },
+
+ 'connection_credentialVerification': function() {
+ return {
+ 'title': Clipperz.PM.Strings['connectionLoginCredentialsVerificationMessageTitle'],
+ 'text': Clipperz.PM.Strings['connectionLoginCredentialsVerificationMessageText']
+ }
+ },
+
+ 'connection_loggedIn': function() {
+ return {
+ 'title': Clipperz.PM.Strings['connectionLoginDoneMessageTitle'],
+ 'text': Clipperz.PM.Strings['connectionLoginDoneMessageText']
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ //
+ // Login message panel - user
+ //
+ 'connection_upgrading': function() {
+ return {
+ 'title': Clipperz.PM.Strings['userLoginPanelUpgradingUserCredentialsMessageTitle'],
+ 'text': Clipperz.PM.Strings['userLoginPanelUpgradingUserCredentialsMessageText'],
+ 'steps': '+1'
+ }
+ },
+
+ 'connection_done': function() {
+ return {
+ 'title': Clipperz.PM.Strings['userLoginPanelConnectedMessageTitle'],
+ 'text': Clipperz.PM.Strings['userLoginPanelConnectedMessageText']
+ }
+ },
+
+ 'connection_tryOlderSchema': function() {
+ return {
+ 'title': Clipperz.PM.Strings['userLoginPanelTryingAnOlderConnectionSchemaMessageTitle'],
+ 'text': Clipperz.PM.Strings['userLoginPanelTryingAnOlderConnectionSchemaMessageText'],
+ 'steps': '+4'
+ }
+ },
+
+ 'connection_loadingUserData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['userLoginPanelLoadingUserDataMessageTitle'],
+ 'text': Clipperz.PM.Strings['userLoginPanelLoadingUserDataMessageText']
+ }
+ },
+
+ 'connection_decryptingUserData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['userLoginPanelDecryptingUserDataMessageTitle'],
+ 'text': Clipperz.PM.Strings['userLoginPanelDecryptingUserDataMessageText'],
+ 'steps': '+1'
+ }
+ },
+
+ 'connection_decryptingUserStatistics': function() {
+ return {
+ 'title': Clipperz.PM.Strings['userLoginPanelDecryptingUserStatisticsMessageTitle'],
+ 'text': Clipperz.PM.Strings['userLoginPanelDecryptingUserStatisticsMessageText']
+ }
+ },
+
+ 'collectingEntropy': function() {
+ return {
+ 'text': Clipperz.PM.Strings['panelCollectingEntryopyMessageText'],
+ 'steps': '+1'
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ //
+ // Cards block - delete card panel
+ //
+ 'deleteRecord_collectData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['deleteRecordPanelCollectRecordDataMessageTitle'],
+ 'text': Clipperz.PM.Strings['deleteRecordPanelCollectRecordDataMessageText']
+ }
+ },
+
+ 'deleteRecord_encryptData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['deleteRecordPanelEncryptUserDataMessageTitle'],
+ 'text': Clipperz.PM.Strings['deleteRecordPanelEncryptUserDataMessageText']
+ }
+ },
+
+ 'deleteRecord_sendingData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['deleteRecordPanelSendingDataToTheServerMessageTitle'],
+ 'text': Clipperz.PM.Strings['deleteRecordPanelSendingDataToTheServerMessageText']
+ }
+ },
+
+ 'deleteRecord_updatingInterface': function() {
+ return {
+ 'title': Clipperz.PM.Strings['deleteRecordPanelUpdatingTheInterfaceMessageTitle'],
+ 'text': Clipperz.PM.Strings['deleteRecordPanelUpdatingTheInterfaceMessageText']
+ }
+ },
+
+
+ //-------------------------------------------------------------------------
+ //
+ // Cards block - save card panel
+ //
+ 'saveCard_collectRecordInfo': function() {
+ return {
+ 'title': Clipperz.PM.Strings['recordSaveChangesPanelCollectRecordInfoMessageTitle'],
+ 'text': Clipperz.PM.Strings['recordSaveChangesPanelCollectRecordInfoMessageText']
+ }
+ },
+
+ 'saveCard_encryptUserData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['recordSaveChangesPanelEncryptUserDataMessageTitle'],
+ 'text': Clipperz.PM.Strings['recordSaveChangesPanelEncryptUserDataMessageText']
+ }
+ },
+
+ 'saveCard_encryptRecordData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['recordSaveChangesPanelEncryptRecordDataMessageTitle'],
+ 'text': Clipperz.PM.Strings['recordSaveChangesPanelEncryptRecordDataMessageText']
+ }
+ },
+
+ 'saveCard_encryptRecordVersions': function() {
+ return {
+ 'title': Clipperz.PM.Strings['recordSaveChangesPanelEncryptRecordVersionDataMessageTitle'],
+ 'text': Clipperz.PM.Strings['recordSaveChangesPanelEncryptRecordVersionDataMessageText']
+ }
+ },
+
+ 'saveCard_sendingData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['recordSaveChangesPanelSendingDataToTheServerMessageTitle'],
+ 'text': Clipperz.PM.Strings['recordSaveChangesPanelSendingDataToTheServerMessageText']
+ }
+ },
+
+ 'saveCard_updatingInterface': function() {
+ return {
+ 'title': Clipperz.PM.Strings['recordSaveChangesPanelUpdatingTheInterfaceMessageTitle'],
+ 'text': Clipperz.PM.Strings['recordSaveChangesPanelUpdatingTheInterfaceMessageText']
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ //
+ // Account panel - user preferences
+ //
+ 'account_savingPreferences_1': function() {
+ return {
+ 'title': Clipperz.PM.Strings['accountPreferencesSavingPanelTitle_Step1'],
+ 'text': Clipperz.PM.Strings['accountPreferencesSavingPanelText_Step1'],
+ 'steps': '+3'
+ }
+ },
+
+ 'account_savingPreferences_2': function() {
+ return {
+ 'title': Clipperz.PM.Strings['accountPreferencesSavingPanelTitle_Step2'],
+ 'text': Clipperz.PM.Strings['accountPreferencesSavingPanelText_Step2']
+ }
+ },
+
+
+ //-------------------------------------------------------------------------
+ //
+ // Account panel - change credentials
+ //
+ 'changeCredentials_encryptingData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['changeCredentialsPanelEncryptingDataMessageTitle'],
+ 'text': Clipperz.PM.Strings['changeCredentialsPanelEncryptingDataMessageText']
+ }
+ },
+
+ 'changeCredentials_creatingNewCredentials': function() {
+ return {
+ 'title': Clipperz.PM.Strings['changeCredentialsPanelCreatingNewCredentialsMessageTitle'],
+ 'text': Clipperz.PM.Strings['changeCredentialsPanelCreatingNewCredentialsMessageText']
+ }
+ },
+
+ 'changeCredentials_sendingCredentials': function() {
+ return {
+ 'title': Clipperz.PM.Strings['changeCredentialsPanelSendingNewCredentialsToTheServerMessageTitle'],
+ 'text': Clipperz.PM.Strings['changeCredentialsPanelSendingNewCredentialsToTheServerMessageText']
+ }
+ },
+
+ 'changeCredentials_done': function() {
+ return {
+ 'title': Clipperz.PM.Strings['changeCredentialsPanelDoneMessageTitle'],
+ 'text': Clipperz.PM.Strings['changeCredentialsPanelDoneMessageText']
+ }
+ },
+
+
+ //-------------------------------------------------------------------------
+ //
+ // Account panel - change credentials
+ //
+ 'saveOTP_encryptUserData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['saveOTP_encryptUserDataTitle'],
+ 'text': Clipperz.PM.Strings['saveOTP_encryptUserDataText'],
+ 'steps': '+4'
+ }
+ },
+
+ 'saveOTP_encryptOTPData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['saveOTP_encryptOTPDataTitle'],
+ 'text': Clipperz.PM.Strings['saveOTP_encryptOTPDataText']
+ }
+ },
+
+ 'saveOTP_sendingData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['saveOTP_sendingDataTitle'],
+ 'text': Clipperz.PM.Strings['saveOTP_sendingDataText']
+ }
+ },
+
+ 'saveOTP_updatingInterface': function() {
+ return {
+ 'title': Clipperz.PM.Strings['saveOTP_updatingInterfaceTitle'],
+ 'text': Clipperz.PM.Strings['saveOTP_updatingInterfaceText']
+ }
+ },
+
+
+ //-------------------------------------------------------------------------
+ //
+ // Data panel - processingImportData
+ //
+ 'parseImportData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['importData_parsingDataTitle'],
+ 'text': Clipperz.PM.Strings['importData_parsingDataText']
+ }
+ },
+
+ 'previewImportData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['importData_previewingDataTitle'],
+ 'text': Clipperz.PM.Strings['importData_previewingDataText']
+ }
+ },
+
+ 'processingImportData': function() {
+ return {
+ 'title': Clipperz.PM.Strings['importData_processingDataTitle'],
+ 'text': Clipperz.PM.Strings['importData_processingDataText']
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+
+}
diff --git a/frontend/delta/js/Clipperz/PM/Strings/Strings_defaults.js b/frontend/delta/js/Clipperz/PM/Strings/Strings_defaults.js
new file mode 100644
index 0000000..aefd94a
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Strings/Strings_defaults.js
@@ -0,0 +1,385 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+if (typeof(Clipperz.PM.Strings) == 'undefined') { Clipperz.PM.Strings = {}; }
+if (typeof(Clipperz.PM.Strings.Languages) == 'undefined') { Clipperz.PM.Strings.Languages = {}; }
+
+//=============================================================================
+//
+// D E F A U L T S ( defaults )
+//
+//=============================================================================
+
+Clipperz.PM.Strings.Languages['defaults'] = {
+
+'elapsedTimeDescriptions': {
+ 'MORE_THAN_A_MONTH_AGO': "more than a month ago",
+ 'MORE_THAN_A_WEEK_AGO': "more than a week ago",
+ 'MORE_THAN_*_WEEKS_AGO': "more than __elapsed__ weeks ago",
+ 'YESTERDAY': "yesterday",
+ '*_DAYS_AGO': "__elapsed__ days ago",
+ 'ABOUT_AN_HOUR_AGO': "about an hour ago",
+ '*_HOURS_AGO': "__elapsed__ hours ago",
+ 'JUST_A_FEW_MINUTES_AGO': "just a few minutes ago",
+ 'ABOUT_*_MINUTES_AGO': "about __elapsed__ minutes ago"
+},
+/*
+'unknown_ip': "unknown",
+
+'countries': {
+ '--': "unknown",
+ 'AD': "Andorra",
+ 'AE': "United Arab Emirates",
+ 'AF': "Afghanistan",
+ 'AG': "Antigua and Barbuda",
+ 'AI': "Anguilla",
+ 'AL': "Albania",
+ 'AM': "Armenia",
+ 'AN': "Netherlands Antilles",
+ 'AO': "Angola",
+ 'AP': "Non-Spec Asia Pas Location",
+ 'AR': "Argentina",
+ 'AS': "American Samoa",
+ 'AT': "Austria",
+ 'AU': "Australia",
+ 'AW': "Aruba",
+ 'AX': "Aland Islands",
+ 'AZ': "Azerbaijan",
+ 'BA': "Bosnia and Herzegowina",
+ 'BB': "Barbados",
+ 'BD': "Bangladesh",
+ 'BE': "Belgium",
+ 'BF': "Burkina Faso",
+ 'BG': "Bulgaria",
+ 'BH': "Bahrain",
+ 'BI': "Burundi",
+ 'BJ': "Benin",
+ 'BM': "Bermuda",
+ 'BN': "Brunei Darussalam",
+ 'BO': "Bolivia",
+ 'BR': "Brazil",
+ 'BS': "Bahamas",
+ 'BT': "Bhutan",
+ 'BW': "Botswana",
+ 'BY': "Belarus",
+ 'BZ': "Belize",
+ 'CA': "Canada",
+ 'CD': "Congo the Democratic Republic of the",
+ 'CF': "Central African Republic",
+ 'CH': "Switzerland",
+ 'CI': "Cote D'ivoire",
+ 'CK': "Cook Islands",
+ 'CL': "Chile",
+ 'CM': "Cameroon",
+ 'CN': "China",
+ 'CO': "Colombia",
+ 'CR': "Costa Rica",
+ 'CS': "Serbia and Montenegro",
+ 'CU': "Cuba",
+ 'CY': "Cyprus",
+ 'CZ': "Czech Republic",
+ 'DE': "Germany",
+ 'DJ': "Djibouti",
+ 'DK': "Denmark",
+ 'DO': "Dominican Republic",
+ 'DZ': "Algeria",
+ 'EC': "Ecuador",
+ 'EE': "Estonia",
+ 'EG': "Egypt",
+ 'ER': "Eritrea",
+ 'ES': "Spain",
+ 'ET': "Ethiopia",
+ 'EU': "European Union",
+ 'FI': "Finland",
+ 'FJ': "Fiji",
+ 'FM': "Micronesia Federated States of",
+ 'FO': "Faroe Islands",
+ 'FR': "France",
+ 'GA': "Gabon",
+ 'GB': "United Kingdom",
+ 'GD': "Grenada",
+ 'GE': "Georgia",
+ 'GF': "French Guiana",
+ 'GG': "Guernsey",
+ 'GH': "Ghana",
+ 'GI': "Gibraltar",
+ 'GL': "Greenland",
+ 'GM': "Gambia",
+ 'GP': "Guadeloupe",
+ 'GR': "Greece",
+ 'GT': "Guatemala",
+ 'GU': "Guam",
+ 'GW': "Guinea-Bissau",
+ 'GY': "Guyana",
+ 'HK': "Hong Kong",
+ 'HN': "Honduras",
+ 'HR': "Croatia (Local Name: Hrvatska)",
+ 'HT': "Haiti",
+ 'HU': "Hungary",
+ 'ID': "Indonesia",
+ 'IE': "Ireland",
+ 'IL': "Israel",
+ 'IM': "Isle of Man",
+ 'IN': "India",
+ 'IO': "British Indian Ocean Territory",
+ 'IQ': "Iraq",
+ 'IR': "Iran (Islamic Republic of)",
+ 'IS': "Iceland",
+ 'IT': "Italy",
+ 'JE': "Jersey",
+ 'JM': "Jamaica",
+ 'JO': "Jordan",
+ 'JP': "Japan",
+ 'KE': "Kenya",
+ 'KG': "Kyrgyzstan",
+ 'KH': "Cambodia",
+ 'KI': "Kiribati",
+ 'KN': "Saint Kitts and Nevis",
+ 'KR': "Korea Republic of",
+ 'KW': "Kuwait",
+ 'KY': "Cayman Islands",
+ 'KZ': "Kazakhstan",
+ 'LA': "Lao People's Democratic Republic",
+ 'LB': "Lebanon",
+ 'LC': "Saint Lucia",
+ 'LI': "Liechtenstein",
+ 'LK': "Sri Lanka",
+ 'LR': "Liberia",
+ 'LS': "Lesotho",
+ 'LT': "Lithuania",
+ 'LU': "Luxembourg",
+ 'LV': "Latvia",
+ 'LY': "Libyan Arab Jamahiriya",
+ 'MA': "Morocco",
+ 'MC': "Monaco",
+ 'MD': "Moldova Republic of",
+ 'MG': "Madagascar",
+ 'MH': "Marshall Islands",
+ 'MK': "Macedonia the Former Yugoslav Republic of",
+ 'ML': "Mali",
+ 'MM': "Myanmar",
+ 'MN': "Mongolia",
+ 'MO': "Macau",
+ 'MP': "Northern Mariana Islands",
+ 'MR': "Mauritania",
+ 'MS': "Montserrat",
+ 'MT': "Malta",
+ 'MU': "Mauritius",
+ 'MV': "Maldives",
+ 'MW': "Malawi",
+ 'MX': "Mexico",
+ 'MY': "Malaysia",
+ 'MZ': "Mozambique",
+ 'NA': "Namibia",
+ 'NC': "New Caledonia",
+ 'NF': "Norfolk Island",
+ 'NG': "Nigeria",
+ 'NI': "Nicaragua",
+ 'NL': "Netherlands",
+ 'NO': "Norway",
+ 'NP': "Nepal",
+ 'NR': "Nauru",
+ 'NU': "Niue",
+ 'NZ': "New Zealand",
+ 'OM': "Oman",
+ 'PA': "Panama",
+ 'PE': "Peru",
+ 'PF': "French Polynesia",
+ 'PG': "Papua New Guinea",
+ 'PH': "Philippines",
+ 'PK': "Pakistan",
+ 'PL': "Poland",
+ 'PR': "Puerto Rico",
+ 'PS': "Palestinian Territory Occupied",
+ 'PT': "Portugal",
+ 'PW': "Palau",
+ 'PY': "Paraguay",
+ 'QA': "Qatar",
+ 'RO': "Romania",
+ 'RS': "Serbia",
+ 'RU': "Russian Federation",
+ 'RW': "Rwanda",
+ 'SA': "Saudi Arabia",
+ 'SB': "Solomon Islands",
+ 'SC': "Seychelles",
+ 'SD': "Sudan",
+ 'SE': "Sweden",
+ 'SG': "Singapore",
+ 'SI': "Slovenia",
+ 'SK': "Slovakia (Slovak Republic)",
+ 'SL': "Sierra Leone",
+ 'SM': "San Marino",
+ 'SN': "Senegal",
+ 'SR': "Suriname",
+ 'SV': "El Salvador",
+ 'SY': "Syrian Arab Republic",
+ 'SZ': "Swaziland",
+ 'TC': "Turks and Caicos Islands",
+ 'TG': "Togo",
+ 'TH': "Thailand",
+ 'TJ': "Tajikistan",
+ 'TM': "Turkmenistan",
+ 'TN': "Tunisia",
+ 'TO': "Tonga",
+ 'TR': "Turkey",
+ 'TT': "Trinidad and Tobago",
+ 'TV': "Tuvalu",
+ 'TW': "Taiwan Province of China",
+ 'TZ': "Tanzania United Republic of",
+ 'UA': "Ukraine",
+ 'UG': "Uganda",
+ 'US': "United States",
+ 'UY': "Uruguay",
+ 'UZ': "Uzbekistan",
+ 'VA': "Holy See (Vatican City State)",
+ 'VE': "Venezuela",
+ 'VG': "Virgin Islands (British)",
+ 'VI': "Virgin Islands (U.S.)",
+ 'VN': "Viet Nam",
+ 'VU': "Vanuatu",
+ 'WF': "Wallis and Futuna Islands",
+ 'WS': "Samoa",
+ 'YE': "Yemen",
+ 'ZA': "South Africa",
+ 'ZM': "Zambia",
+ 'ZW': "Zimbabwe",
+ 'ZZ': "Reserved"
+},
+
+'browsers': {
+ 'UNKNOWN': "Unknown",
+ 'MSIE': "Internet Explorer",
+ 'FIREFOX': "Firefox",
+ 'OPERA': "Opera",
+ 'SAFARI': "Safari",
+ 'OMNIWEB': "OmniWeb",
+ 'CAMINO': "Camino",
+ 'CHROME': "Chrome"
+},
+
+'operatingSystems': {
+ 'UNKNOWN': "Unknown",
+ 'WINDOWS': "Windows",
+ 'MAC': "Mac",
+ 'LINUX': "Linux",
+ 'IPHONE': "iPhone",
+ 'MOBILE': "Mobile",
+ 'OPENBSD': "OpenBSD",
+ 'FREEBSD': "FreeBSD",
+ 'NETBSD': "NetBSD"
+},
+*/
+
+// Calendar texts
+'calendarStrings': {
+ 'months': {
+ '0': "January",
+ '1': "February",
+ '2': "March",
+ '3': "April",
+ '4': "May",
+ '5': "June",
+ '6': "July",
+ '7': "August",
+ '8': "September",
+ '9': "October",
+ '10': "November",
+ '11': "December"
+ },
+ 'shortMonths': {
+ '0': "Jan",
+ '1': "Feb",
+ '2': "Mar",
+ '3': "Apr",
+ '4': "May",
+ '5': "Jun",
+ '6': "Jul",
+ '7': "Aug",
+ '8': "Sep",
+ '9': "Oct",
+ '10': "Nov",
+ '11': "Dec"
+ },
+
+ 'days': {
+ '0': "Sunday",
+ '1': "Monday",
+ '2': "Tuesday",
+ '3': "Wednesday",
+ '4': "Thursday",
+ '5': "Friday",
+ '6': "Saturday"
+ },
+
+ 'shortDays': {
+ '0': "Sun",
+ '1': "Mon",
+ '2': "Tue",
+ '3': "Wed",
+ '4': "Thu",
+ '5': "Fri",
+ '6': "Sat"
+ },
+
+ 'veryShortDays': {
+ '0': "Su",
+ '1': "Mo",
+ '2': "Tu",
+ '3': "We",
+ '4': "Th",
+ '5': "Fr",
+ '6': "Sa"
+ },
+
+ 'amDesignation': "am",
+ 'pmDesignation': "pm"
+
+},
+
+// Date format
+'fullDate_format': "l, F d, Y H:i:s",
+
+//################################################################################
+
+'pageHeader': {
+ 'donation': "donate",
+ 'forum': "forum",
+ 'credits': "credits",
+ 'feedback': "feedback",
+ 'help': "help"
+},
+
+'bookmarkletCopy': {
+ 'noExceptionMessage': "The direct login configuration has been collected.",
+ 'exceptionMessage': "Sorry! There was an error while processing the page.",
+ 'copy': "copy",
+ 'successfulMessage': "DONE!",
+ 'failMessage': "Failed! :("
+},
+
+//################################################################################
+
+__syntaxFix__: "syntax fix"
+}
diff --git a/frontend/delta/js/Clipperz/PM/Strings/Strings_en-US.js b/frontend/delta/js/Clipperz/PM/Strings/Strings_en-US.js
new file mode 100644
index 0000000..72460ba
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Strings/Strings_en-US.js
@@ -0,0 +1,1336 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+//=============================================================================
+//
+// E N G L I S H A M E R I C A N ( en_US )
+//
+//=============================================================================
+
+Clipperz.PM.Strings.Languages['en-us'] = {
+/*
+// Login page - description
+'clipperzServiceDescription': "\
+ <!-- FIX CSS DONE --> \
+ <h2>Keep it to yourself!</h2>\
+ <ul>\
+ <li>\
+ <h3>Clipperz is:</h3>\
+ <ul>\
+ <li><p>a secure and simple password manager</p></li>\
+ <li><p>an effective single sign-on solution</p></li>\
+ <li><p>a digital vault for your personal data</p></li>\
+ </ul>\
+ </li>\
+ <li>\
+ <h3>With Clipperz you can:</h3>\
+ <ul>\
+ <li><p>store and manage your passwords and online credentials</p></li>\
+ <li><p>login to your web services without entering any username or password</p></li>\
+ <li><p>protect all your sensitive data: codes for burglar alarms, PINs, credit card numbers, …</p></li>\
+ <li><p>share secrets with family members and associates (coming soon)</p></li>\
+ </ul>\
+ </li>\
+ <li>\
+ <h3>Clipperz benefits:</h3>\
+ <ul>\
+ <li><p>free and completely anonymous</p></li>\
+ <li><p>access it any time from any computer</p></li>\
+ <li><p>no software to download and nothing to install</p></li>\
+ <li><p>avoid keeping secrets on your PC or on paper</p></li>\
+ </ul>\
+ </li>\
+ <li>\
+ <h3>Clipperz security:</h3>\
+ <ul>\
+ <li><p>your secrets are locally encrypted by your browser before being uploaded to Clipperz</p></li>\
+ <li><p>the encryption key is a passphrase known only to you</p></li>\
+ <li><p>Clipperz hosts your sensitive data in encrypted form and could never actually access the data in its plain form</p></li>\
+ <li><p>Clipperz is built upon standard encryption schemes, nothing fancies or homemade</p></li>\
+ <li><p>you can review the source code anytime you like, but you need to know nothing about cryptography to be an happy user!</p></li>\
+ </ul>\
+ </li>\
+ <li>\
+ <a href=\"http://www.clipperz.com\" target=\"_blank\">Learn more</a>\
+ </li>\
+ </ul>",
+
+
+'loginFormTitle': "login with your Clipperz account",
+'loginFormUsernameLabel': "username",
+'loginFormPassphraseLabel': "passphrase",
+'loginFormDontHaveAnAccountLabel': "don\'t have an account?",
+'loginFormCreateOneLabel': "create one",
+'loginFormForgotYourCredentialsLabel': "forgot your credentials?",
+'loginFormAarghThatsBadLabel': "aargh! that\'s bad!",
+'loginFormAfraidOfMaliciousScriptsLabel': "afraid of malicious scripts?",
+'loginFormVerifyTheCodeLabel': "verify the code",
+'loginFormButtonLabel': "Login",
+'loginFormOneTimePasswordCheckboxLabel': "use a one-time passphrase",
+'loginFormOneTimePasswordCheckboxDescription': "",
+
+// Login page - language selection
+'loginPanelSwithLanguageDescription': "<h5>Switch to your preferred language</h5>",
+
+// Login page - browser compatibility
+'browserCompatibilityDescription': "<p>Have a better and safer Clipperz experience with Firefox. However Clipperz works just fine also with Opera, Safari and MS Internet Explorer!</p>",
+
+// Login with OTP - message panel
+'OTPloginMessagePanelInitialTitle': "Logging in using a one-time passphrase",
+'OTPloginMessagePanelInitialText': "Sending OTP credentials …",
+'OTPloginMessagePanelLoadingTitle': "Logging in using a one-time passphrase",
+'OTPloginMessagePanelLoadingText': "Fetching encrypted authentication data from the server …",
+'OTPloginMessagePanelProcessingTitle': "Logging in using a one-time passphrase",
+'OTPloginMessagePanelProcessingText': "Local decryption of authentication data",
+
+// Regular login - message panel
+'loginMessagePanelInitialTitle': "Logging in …",
+'loginMessagePanelInitialText': "---",
+'loginMessagePanelInitialButtonLabel': "Cancel",
+'loginMessagePanelConnectedTitle': "Connected",
+'loginMessagePanelConnectedText': "Done",
+'loginMessagePanelFailureTitle': "Error",
+'loginMessagePanelFailureText': "Login failed",
+'loginMessagePanelFailureButtonLabel': "Close",
+
+// Regular login - message panel - connection
+'connectionLoginSendingCredentialsMessageTitle': "Verifying credentials",
+'connectionLoginSendingCredentialsMessageText': "Sending credentials",
+'connectionLoginCredentialsVerificationMessageTitle': "Verifying credentials",
+'connectionLoginCredentialsVerificationMessageText': "Performing SRP authentication",
+'connectionLoginDoneMessageTitle': "Verifying credentials",
+'connectionLoginDoneMessageText': "Connected",
+
+// Regular login - message panel - user
+'userLoginPanelUpgradingUserCredentialsMessageTitle': "Verifying credentials",
+'userLoginPanelUpgradingUserCredentialsMessageText': "Upgrading your credentials to a new authentication schema",
+'userLoginPanelConnectedMessageTitle': "User authenticated",
+'userLoginPanelConnectedMessageText': "Successfully logged in",
+'userLoginPanelTryingAnOlderConnectionSchemaMessageTitle': "Verifying credentials",
+'userLoginPanelTryingAnOlderConnectionSchemaMessageText': "Trying an older authentication schema",
+'userLoginPanelLoadingUserDataMessageTitle': "User authenticated",
+'userLoginPanelLoadingUserDataMessageText': "Downloading encrypted card headers from Clipperz",
+'userLoginPanelDecryptingUserDataMessageTitle': "User authenticated",
+'userLoginPanelDecryptingUserDataMessageText': "Local decryption of card headers",
+'userLoginPanelDecryptingUserStatisticsMessageTitle': "User authenticated",
+'userLoginPanelDecryptingUserStatisticsMessageText': "Local decryption of usage statistics",
+
+// Registration page - splash alert
+'splashAlertTitle': "Welcome to Clipperz!",
+'splashAlertText': "\
+ <!-- FIX CSS DONE! --> \
+ <p>Some security advice</p>\
+ <ul>\
+ <li><p>Storing your data at Clipperz is as secure as the passphrase you choose to protect them. Nobody can access them unless they know your passphrase.</p></li>\
+ <li><p>If you are going to use Clipperz for safeguarding sensitive and critical information please make sure to use a strong passphrase. The longer the better!</p></li>\
+ <li><p>Clipperz will not be able to recover a lost passphrase!</p></li>\
+ </ul>\
+ <p>For any further information, please refer to <a href=\"http://www.clipperz.com\" target=\"_blank\">Clipperz</a> website.</p>",
+'splashAlertCloseButtonLabel': "Ok",
+
+// Registration page - form
+'registrationFormTitle': "create your account",
+'registrationFormUsernameLabel': "username",
+'registrationFormPassphraseLabel': "passphrase",
+'registrationFormRetypePassphraseLabel': "re-enter passphrase",
+'registrationFormSafetyCheckLabel': "I understand that Clipperz will not be able to recover a lost passphrase.",
+'registrationFormTermsOfServiceCheckLabel': "I have read and agreed to the <a href='https://www.clipperz.com/terms_service' target='_blank'>Terms of Service</a>.",
+'registrationFormDoYouAlreadyHaveAnAccountLabel': "do you already have an account?",
+'registrationFormSimplyLoginLabel': "simply login",
+'registrationFormButtonLabel': "Register",
+
+// Registration page - warning messages
+'registrationFormWarningMessageNotMatchingPassphrases': "Your passphrases don't match, please re-type them.",
+'registrationFormWarningMessageSafetyCheckNotSelected': "Please read and check all the boxes below.",
+'registrationFormWarningMessageTermsOfServiceCheckNotSelected': "You need to agree to the Terms of Service.",
+
+// Registration page - message panel
+'registrationMessagePanelInitialTitle': "Creating account …",
+'registrationMessagePanelInitialText': "---",
+'registrationMessagePanelInitialButtonLabel': "Cancel",
+'registrationMessagePanelRegistrationDoneTitle': "Registration",
+'registrationMessagePanelRegistrationDoneText': "Done",
+'registrationMessagePanelFailureTitle': "Registration failed",
+'registrationMessagePanelFailureButtonLabel': "Close",
+
+// Registration page - message panel - connection
+'connectionRegistrationSendingRequestMessageText': "Verifying credentials",
+'connectionRegistrationSendingCredentialsMessageText': "Sending credentials",
+
+// Registration page - splash panel
+'registrationSplashPanelTitle': "Security advice",
+'registrationSplashPanelDescription': "<p>These are your Clipperz credentials, take good care of them. Clipperz will never display your username and passphrase a second time!</p>",
+'registrationSplashPanelUsernameLabel': "username",
+'registrationSplashPanelPassphraseLabel': "passphrase",
+
+'registrationSplashPanelShowPassphraseButtonLabel': "show passphrase",
+
+// Header links
+'donateHeaderLinkLabel': "donate",
+'creditsHeaderLinkLabel': "credits",
+'feedbackHeaderLinkLabel': "feedback",
+'helpHeaderLinkLabel': "help",
+'forumHeaderLinkLabel': "forum",
+
+// Menu labels
+'recordMenuLabel': "cards",
+'accountMenuLabel': "account",
+'dataMenuLabel': "data",
+'contactsMenuLabel': "contacts",
+'toolsMenuLabel': "tools",
+'logoutMenuLabel': "logout",
+'lockMenuLabel': "lock",
+
+// Lock dialog
+'lockTitle': "The account is locked",
+'lockDescription': "<p>To unlock your account, please enter your passphrase.</p>",
+'unlockButtonLabel': "Unlock",
+
+// Account panel - change passphrase
+'changePasswordTabLabel': "Change your passphrase",
+'changePasswordTabTitle': "Change your passphrase",
+
+'changePasswordFormUsernameLabel': "username",
+'changePasswordFormOldPassphraseLabel': "old passphrase",
+'changePasswordFormNewPassphraseLabel': "new passphrase",
+'changePasswordFormRetypePassphraseLabel': "re-enter new passphrase",
+'changePasswordFormSafetyCheckboxLabel': "I understand that Clipperz will not be able to recover a lost passphrase.",
+'changePasswordFormSubmitLabel': "Change passphrase",
+
+// Account panel - change passphrase - warning messages
+'changePasswordFormWrongUsernameWarning': "Wrong username",
+'changePasswordFormWrongPassphraseWarning': "Wrong passphrase",
+'changePasswordFormWrongRetypePassphraseWarning': "Your passphrases don't match, please re-type them.",
+'changePasswordFormSafetyCheckWarning': "Please read and check the box below.",
+
+// Account panel - change passphrase - progress dialog
+'changePasswordFormProgressDialogTitle': "Changing user credentials",
+'changePasswordFormProgressDialogEmptyText': "---",
+'changePasswordFormProgressDialogConnectedMessageTitle': "Connected",
+'changePasswordFormProgressDialogConnectedMessageText': "Done",
+'changePasswordFormProgressDialogErrorMessageTitle': "Error",
+'changePasswordFormProgressDialogErrorMessageText': "Credentials change failed!",
+
+'changeCredentialsPanelEncryptingDataMessageTitle': "Changing your passphrase",
+'changeCredentialsPanelEncryptingDataMessageText': "Local encryption of card headers",
+'changeCredentialsPanelCreatingNewCredentialsMessageTitle': "Changing your passphrase",
+'changeCredentialsPanelCreatingNewCredentialsMessageText': "Updating your credentials",
+'changeCredentialsPanelSendingNewCredentialsToTheServerMessageTitle': "Changing your passphrase",
+'changeCredentialsPanelSendingNewCredentialsToTheServerMessageText': "Uploading your encrypted credentials to Clipperz",
+'changeCredentialsPanelDoneMessageTitle': "Changing your passphrase",
+'changeCredentialsPanelDoneMessageText': "Done",
+
+// Account panel - OTP
+'manageOTPTabLabel': "Manage your one-time passphrases",
+'manageOTPTabTitle': "Manage your one-time passphrases",
+
+'manageOTPTabDescription': "\
+ <p>A one-time passphrase works like your regular passphrase, but can be used only once.</p>\
+ <p>If the same passphrase is used again at a later stage in a login attempt it will be rejected and the login process will fail.</p>\
+ <p>Immediately after a successful login, your one-time passphrase will be deleted preventing any fraudulent access.</p>\
+ <p>One-time passphrases are an excellent choice if one is concerned about keyloggers or spyware infections that may be collecting data from compromised machines.</p>\
+ <p><b>It's strongly advisable to use one-time passphrases when accessing Clipperz from public terminals, such as Internet cafes and libraries.</b></p>",
+
+// Account panel - OTP - OTP table
+'oneTimePasswordReadOnlyMessage': "\
+ <h6>Sorry!</h6>\
+ <p>You cannot manage your one-time passphrases when using the offline version of Clipperz.</p>",
+
+'oneTimePasswordLoadingMessage': "\
+ <h6>Loading data</h6>\
+ <p>Please wait …</p>",
+
+'oneTimePasswordNoPasswordAvailable': "\
+ <h6>No one-time passphrase available</h6>\
+ <p>Click the “New” button above to add one-time passphrases to your account.</p>",
+
+'createNewOTPButtonLabel': "New",
+'deleteOTPButtonLabel': "Delete",
+'printOTPButtonLabel': "Print",
+
+'disabledOneTimePassword_warning': "disabled",
+
+'oneTimePasswordSelectionLink_selectLabel': "Select:",
+'oneTimePasswordSelectionLink_all': "all",
+'oneTimePasswordSelectionLink_none': "none",
+'oneTimePasswordSelectionLink_used': "used",
+'oneTimePasswordSelectionLink_unused': "unused",
+
+//Account panel - OTP - saving new OTP dialog
+'saveOTP_encryptUserDataTitle': "Saving one-time passphrase",
+'saveOTP_encryptUserDataText': "Processing new OTP credentials …",
+'saveOTP_encryptOTPDataTitle': "Saving one-time passphrase",
+'saveOTP_encryptOTPDataText': "Local encryption of authentication data …",
+'saveOTP_sendingDataTitle': "Saving one-time passphrase",
+'saveOTP_sendingDataText': "Sending authentication data to the server …",
+'saveOTP_updatingInterfaceTitle': "Saving one-time passphrase",
+'saveOTP_updatingInterfaceText': "Updating interface",
+
+// Account panel - preferences
+'accountPreferencesLabel': "Preferences",
+'accountPreferencesTabTitle': "Preferences",
+
+'accountPreferencesLanguageTitle': "Language",
+'accountPreferencesLanguageDescription': "<p>Choose your preferred language from the list below.</p>",
+
+'showDonationReminderPanelTitle': "Donation reminders",
+'showDonationReminderPanelDescription': "<p>Show donation reminders</p>",
+
+'saveUserPreferencesFormSubmitLabel': "Save",
+'cancelUserPreferencesFormSubmitLabel': "Cancel",
+
+// Account panel - preferences - saving dialog
+'accountPreferencesSavingPanelTitle_Step1': "Saving preferences",
+'accountPreferencesSavingPanelText_Step1': "Local encryption of your preferences",
+'accountPreferencesSavingPanelTitle_Step2': "Saving preferences",
+'accountPreferencesSavingPanelText_Step2': "Sending encrypted preferences to Clipperz",
+
+// Account panel - login history
+'accountLoginHistoryLabel': "Login history",
+'loginHistoryTabTitle': "Login history",
+
+'loginHistoryReadOnlyMessage': "\
+ <h6>Sorry!</h6>\
+ <p>The login history is not available while using the offline version of Clipperz.</p>",
+
+'loginHistoryLoadingMessage': "\
+ <h6>Loading data</h6>\
+ <p>Please wait …</p>",
+
+'loginHistoryLoadedMessage': "\
+ <h6>Your latest 10 logins</h6>\
+ <p></p>",
+
+'loginHistoryIPLabel': "IP",
+'loginHistoryTimeLabel': "date",
+'loginHistoryCurrentSessionText': "current session",
+'loginHistoryReloadButtonLabel': "Reload login history",
+
+// Account panel - delete account
+'deleteAccountTabLabel': "Delete your account",
+'deleteAccountTabTitle': "Delete your account",
+
+'deleteAccountFormUsernameLabel': "username",
+'deleteAccountFormPassphraseLabel': "passphrase",
+'deleteAccountFormSafetyCheckboxLabel': "I understand that all my data will be deleted and that this action is irreversible.",
+'deleteAccountFormSubmitLabel': "Delete my account",
+
+//Account panel - delete account - warnings
+'deleteAccountFormWrongUsernameWarning': "Wrong username",
+'deleteAccountFormWrongPassphraseWarning': "Wrong passphrase",
+'deleteAccountFormSafetyCheckWarning': "Please read and check the box below.",
+
+//Account panel - delete account - confirmation
+'accountPanelDeletingAccountPanelConfirmationTitle': "ATTENTION",
+'accountPanelDeleteAccountPanelConfirmationText': "Are your sure you want to delete your account?",
+'accountPanelDeleteAccountPanelConfirmButtonLabel': "Yes",
+'accountPanelDeleteAccountPanelDenyButtonLabel': "No",
+
+//Account panel - delete account - confirmation
+'accountPanelDeletingAccountPanelProgressTitle': "Deleting the account data",
+'accountPanelDeletingAccountPanelProgressText': "The operation could take long, please be patient.",
+
+//Data panel - offline copy
+'offlineCopyTabLabel': "Offline copy",
+'offlineCopyTabTitle': "Offline copy",
+
+'offlineCopyTabDescription': "\
+ <!-- FIX CSS DONE! --> \
+ <p>With just one click you can dump all your encrypted data from Clipperz servers to your hard disk and create a read-only offline version of Clipperz to be used when you are not connected to the Internet.</p>\
+ <p>The read-only version is as secure as the read-and-write one and will not expose your data to higher risks since they both share the same code and security architecture.</p>\
+ <ol>\
+ <li><p>Click the link below to start the download.</p></li>\
+ <li><p>The browser will ask you what to do with the “Clipperz_YYYYMMDD.html” file. Save it on your hard disk.</p></li>\
+ <li><p>Double click on the downloaded file to launch the offline version in your browser.</p></li>\
+ <li><p>Enter the usual username and passphrase.</p></li>\
+ </ol>",
+
+'offlineCopyDownloadLinkLabel': "Download",
+
+// Data panel - offline copy - not updated
+'offlineCopyDownloadWarning': "\
+ <!-- FIX CSS DONE! --> \
+ <h4><a href=\"#\" id=\"offlineCopyDownloadWarningLink\">Update your “offline copy”!</a></h4>\
+ <p>You have recently created or modified one or more cards, it would be wise to download a new copy of the offline version.</p>",
+
+'offlineCopyDownloadOk': "",
+
+// Data panel - sharing
+'sharingTabLabel': "Sharing",
+'sharingTabTitle': "Sharing",
+
+'sharingTabDescription': "\
+ <p>Quite often a confidential piece of information needs to be shared with one or more persons.</p>\
+ <p>This could be as simple as giving your colleague the access code of your voice mailbox when you are out of the office, or as complicated as enabling the entitled heirs to access your safe deposit box at the local bank when you pass on.</p>\
+ <p>Clipperz can make sharing your secrets a secure and straightforward process.</p>\
+ <p></p>\
+ <p><b>Coming soon …</b></p>",
+
+// Data panel - import
+'importTabLabel': "Import",
+'importTabTitle': "Import",
+
+'importTabDescription': "<p>You can bulk import data to your Clipperz account from several file formats.</p>",
+
+// Data panel - export
+'printingTabLabel': "Export",
+'printingTabTitle': "Export",
+
+'printingTabDescription': "\
+ <h5>Printing</h5>\
+ <p>Click on the link below to open a new window displaying all your cards in a printable format.</p>\
+ <p>If you are going to print for backup purposes, please consider the safer option provided by the “offline copy”.</p>",
+
+'printingLinkLabel': "Printable version",
+
+'exportTabDescription': "\
+ <h5>Exporting to JSON</h5>\
+ <p>JSON enables a “lossless” export of your cards. All the information will be preserved, including direct login configurations.</p>\
+ <p>This custom format it’s quite convenient if you need to move some of all of your cards to a different Clipperz account. Or if you want to restore a card that has been accidentally deleted.</p>\
+ <p>Click on the link below to start the export process.</p>",
+
+'exportLinkLabel': "Export to JSON",
+
+'exportDataInProgressDescription': "<h4>Exporting, please wait while your data are being processed …</h4>",
+
+'exportDataDescription': "\
+ <h4>Instructions</h4>\
+ <p>Copy the text below to your favorite editor and save it. (e.g. “clipperz_export_20071217.json”)</p>",
+
+// Contacts panel
+'contactsTabLabel': "Contacts",
+'contactsTabTitle': "Contacts",
+
+//Tools panel - password generator
+'passwordGeneratorTabLabel': "Password generator",
+'bookmarkletTabLabel': "Bookmarklet",
+'compactTabLabel': "Compact edition",
+'httpAuthTabLabel': "HTTP authentication",
+
+'passwordGeneratorTabTitle': "Password generator",
+'bookmarkletTabTitle': "Bookmarklet",
+'compactTabTitle': "Compact edition",
+'httpAuthTabTitle': "HTTP authentication",
+
+
+// Tools panel - password generator - description
+'paswordGeneratorTabDescription': "<p></p>",
+'passwordGeneratorTabButtonLabel': "Generate password",
+
+// Tools panel - bookmarklet
+'bookmarkletTabLabel': "Bookmarklet",
+'bookmarkletTabTitle': "Bookmarklet",
+
+'bookmarkletTabDescription': "\
+ <!-- FIX CSS DONE! --> \
+ <p>A bookmarklet is a simple “one-click” tool that can perform very useful tasks. It can be saved and used like a normal web page bookmark.</p>\
+ <p>The Clipperz bookmarklet will help you to quickly create new cards and new “direct logins” within existing cards.</p>\
+ <p><b>Please note that the bookmarklet does not include any information related to your account (e.g. your username or passphrase), the bookmarklet is a general tool containing the same code for every Clipperz user.</b></p>\
+ <h3>How to install the bookmarklet</h3>\
+ <h>Firefox, Camino, Opera, Safari</h5>\
+ <ol>\
+ <li><p>Make sure that the “Bookmarks Bar” is displayed by selecting “View > Toolbars > Bookmarks”, or similar menu items, from the browser menu.</p></li>\
+ <li><p>Drag and drop the “Add to Clipperz” link below to the bookmark bar.</p></li>\
+ </ol>\
+ \
+ <h5>Internet Explorer</h5>\
+ <ol>\
+ <li><p>Make sure that the “Links” toolbar is displayed by selecting “View > Toolbars > Links” from the browser menu.</p></li>\
+ <li><p>Right-click on the “Add to Clipperz” link below.</p></li>\
+ <li><p>Select “Add to favorites” from the contextual menu.</p></li>\
+ <li><p>Click “Yes” for any security message that pops up.</p></li>\
+ <li><p>Open the “Links” folder and click “OK”</p></li>\
+ </ol>",
+
+'bookmarkletTabBookmarkletTitle': "Add to Clipperz",
+
+// Tools panel - bookmarklet - instructions
+'bookmarkletTabInstructions': "\
+ <!-- FIX CSS DONE! --> \
+ <h3>How to create a new card inclusive of a “direct login” link to an online service</h3>\
+ <ol>\
+ <li><p>Open the web page where the login form is hosted. (this is the page where you usually enter your sign-in credentials)</p></li>\
+ <li><p>Launch the bookmarklet by clicking on it: a pop-up window will appear over the web page.</p></li>\
+ <li><p>Copy to the clipboard the content of the large text area within the pop-up. (ctrl-C)</p></li>\
+ <li><p>Enter your Clipperz account and click on the <b>Add new card</b> button.</p></li>\
+ <li><p>Select the “Direct login” template and paste the content of the clipboard to the large text area in the form. (ctrl-V)</p></li>\
+ <li><p>Press the <b>Create</b> button, complete and review the details, then click <b>Save</b>.</p></li>\
+ </ol>\
+ \
+ <h3>How to add a “direct login” link to an existing card</h3>\
+ <ol>\
+ <li><p>Same as above.</p></li>\
+ <li><p>Same as above.</p></li>\
+ <li><p>Same as above.</p></li>\
+ <li><p>Enter your Clipperz account and select the card containing the credentials for the web service you just visited and click the <b>Edit</b> button.</p></li>\
+ <li><p>Paste the content of the clipboard to the large text area in the “Direct logins” section. (ctrl-V)</p></li>\
+ <li><p>Press the <b>Add direct login</b> button, review the details and then click <b>Save</b>.</p></li>\
+ </ol>\
+ \
+ <p></p>\
+ <p>Further information about the bookmarklet are <a href=\"http://www.clipperz.com/support/user_guide/bookmarklet\" target=\"_blank\">available here</a>.</p>",
+
+// Tools panel - Compact - instructions
+'compactTabDescription': "\
+ <!-- FIX CSS DONE! --> \
+ <p>Clipperz Compact is a special version of Clipperz designed to be opened in the Firefox sidebar.</p>\
+ <p>Its purpose is to keep your collection of “direct logins” always at hand. Read more <a href=\"http://www.clipperz.com/support/user_guide/clipperz_compact\", target=\"blank\">here</a></p>\
+ \
+ <h3>How to launch Clipperz Compact in the sidebar</h3>\
+ <ol>\
+ <li><p>Get Firefox! Sidebars are only available in Firefox and you need to switch to Firefox in order to enjoy the convenience of Clipperz Compact.</p></li>\
+ <li>\
+ <p>Add the following URL to Firefox bookmarks, or even better, drag it to the bookmark bar.</p>\
+ <div id=\"compactLinkBox\"><a href=\"https://www.clipperz.com/beta/index.html?compact\" target=\"_search\">Clipperz Compact</a></div>\
+ </li>\
+ <li><p>Change the properties of the bookmark so that “load this bookmark in the sidebar” is checked.</p></li>\
+ </ol>\
+ \
+ <h5>Added bonus: Clipperz Compact works also in Opera’s panel.</h5>",
+
+// Tools panel - HTTP authentication - instructions
+'httpAuthTabDescription': "\
+ <!-- FIX CSS DONE! --> \
+ <p>HTTP authentication is a method designed to allow a web browser to provide credentials – in the form of a username and password – including them in a website address (HTTP or HTTPS URL).</p>\
+ <p>Nowadays it is rarely used, but it can still be found on small, private websites. You can tell that a website is protected by HTTP authentication when the browser displays a pop-up window to enter username and password.</p>\
+ <p>Unfortunately the Clipperz bookmarklet does not work on websites that use HTTP authentication. However you can still create a “direct login”.</p>\
+ \
+ <h3>How to create a “direct login” for a website that uses HTTP authentication</h3>\
+ <ol>\
+ <li><p>Store website URL, username and password in a new card.</p></li>\
+ <li><p>Copy the configuration below and paste it to the large text area in the “Direct logins” section of the new card.</p></li>\
+ <li><p>Press the <b>Add direct login</b> button, bind URL, username and password fields and then click <b>Save</b>.</p></li>\
+ </ol>\
+ \
+ <h5><a href=\"http://support.microsoft.com/kb/834489\" target=\"_blank\">Warning: Internet Explorer does not support HTTP authentication.</a></h5>",
+
+// Direct logins block
+'mainPanelDirectLoginBlockLabel': "Direct logins",
+'directLinkReferenceShowButtonLabel': "show",
+
+// Direct logins - blank slate
+'mainPanelDirectLoginBlockDescription': "\
+ <!-- FIX CSS DONE! --> \
+ <p>Add “direct logins” to sign in to your web accounts without typing usernames and passwords!</p>\
+ <p>“Direct logins” greatly enhance your password security since you can:</p>\
+ <ul>\
+ <li><p>conveniently adopt and enter complex passwords;</p></li>\
+ <li><p>never re-use the same and easy-to-guess password.</p></li>\
+ </ul>\
+ <p>Simple and quick configuration with the <b>Clipperz bookmarklet</b>.</p>\
+ <a href=\"http://www.clipperz.com/support/user_guide/direct_logins\" target=\"_blank\">Learn more about “direct logins”</a>",
+
+// Cards block
+'mainPanelRecordsBlockLabel': "Cards",
+'mainPanelAddRecordButtonLabel': "Add new card",
+'mainPanelRemoveRecordButtonLabel': "Delete card",
+
+// Cards block - filter tabs
+'mainPanelRecordFilterBlockAllLabel': "all",
+'mainPanelRecordFilterBlockTagsLabel': "tags",
+'mainPanelRecordFilterBlockSearchLabel': "search",
+
+// Cards block - blank slate
+'recordDetailNoRecordAtAllTitle': "Welcome to Clipperz!",
+'recordDetailNoRecordAtAllDescription': "\
+ <h5>Get started by adding cards to your account.</h5>\
+ <p>Cards are simple and flexible forms where you can store your passwords and any other confidential data.</p>\
+ <p>Cards could contain credentials for accessing a web site, the combination of your bicycle lock, details of your credit card, …</p>\
+ \
+ <h5>Don't forget the Clipperz bookmarklet!</h5>\
+ <p>Before you start, install the “Add to Clipperz” bookmarklet: it will make creating cards easier and more fun.</p>\
+ <p>Go to the “Tools” tab to discover how to install it and how it use it.</p>\
+ <p></p>\
+ <p>Then simply click the <b>\"Add new card\"</b> button and enjoy your Clipperz account.</p>\
+ <p></p>\
+ <a href=\"http://www.clipperz.com/support/user_guide/managing_cards\" target=\"_blank\">Learn more about creating and managing cards</a>",
+
+// Cards block - new card wizard - bookmarklet configuration
+'newRecordWizardTitleBox': "\
+ <h5>Please select a template</h5>\
+ <p>Cards are simple and flexible forms where you can store passwords or any other confidential data.</p>\
+ <p>Start choosing one of the templates below. You can always customize your cards later by adding or removing fields.</p>",
+
+'newRecordWizardBookmarkletConfigurationTitle': "Direct login",
+'newRecordWizardBookmarkletConfigurationDescription': "\
+ <p>Paste below the configuration code generated by the Clipperz bookmarklet.</p>\
+ <p>A new card complete with a direct login to your web account will be created.</p>",
+
+'newRecordWizardCreateButtonLabel': "Create",
+'newRecordWizardCancelButtonLabel': "Cancel",
+
+// Create new card - Donation splash
+'donateSplashPanelTitle': "Support Clipperz, make a donation today!",
+'donateSplashPanelDescription': "\
+ <!-- FIX CSS DONE! --> \
+ <p>A few good reasons to make a donation:</p>\
+ <ul>\
+ <li><p>support the development of new features</p></li>\
+ <li><p>keep Clipperz free</p></li>\
+ <li><p>show appreciation for our hard work</p></li>\
+ </ul>\
+ <p>For any further information, please visit our <a href=\"http://www.clipperz.com/donations\" target=\"_blank\">Donations page</a>.</p>\
+ <p><b>Ready to donate?</b></p>",
+
+'donateCloseButtonLabel': "Not yet",
+'donateDonateButtonLabel': "Yes",
+
+// Card templates
+'recordTemplates': {
+
+//Web password
+ 'WebAccount': {
+ 'title': "Web password",
+ 'description': "<p>A simple card to store login credentials for your online services.</p>",
+ 'fields': [
+ {label:"Web address", type:'URL'},
+ {label:"Username or email", type:'TXT'},
+ {label:"Password", type:'PWD'}
+ ]
+ },
+
+//Bank account
+ 'BankAccount': {
+ 'title': "Bank account",
+ 'description': "<p>Safely store your bank account number and online banking credentials.</p>",
+ 'fields': [
+ {label:"Bank", type:'TXT'},
+ {label:"Account number", type:'TXT'},
+ {label:"Bank website", type:'URL'},
+ {label:"Online banking ID", type:'TXT'},
+ {label:"Online banking password", type:'PWD'}
+ ]
+ },
+
+// Credit card
+ 'CreditCard': {
+ 'title': "Credit card",
+ 'description': "<p>Card number, expire date, CVV2 and PIN always at hand with Clipperz.</p>",
+ 'fields': [
+ {label:"Type (Visa, AmEx, …)", type:'TXT'},
+ {label:"Number", type:'TXT'},
+ {label:"Owner name", type:'TXT'},
+ {label:"Expiry date", type:'TXT'},
+ {label:"CVV2", type:'TXT'},
+ {label:"PIN", type:'PWD'},
+ {label:"Card website", type:'URL'},
+ {label:"Username", type:'TXT'},
+ {label:"Password", type:'PWD'}
+ ]
+ },
+
+// Address book entry
+ 'AddressBookEntry': {
+ 'title': "Address book entry",
+ 'description': "<p>Clipperz could also work as your new private address book. Use this template to easily add a new entry.</p>",
+ 'fields': [
+ {label:"Name", type:'TXT'},
+ {label:"Email", type:'TXT'},
+ {label:"Phone", type:'TXT'},
+ {label:"Mobile", type:'TXT'},
+ {label:"Address", type:'ADDR'}
+ ]
+ },
+
+//Custom card
+ 'Custom': {
+ 'title': "Custom card",
+ 'description': "<p>No matter which kind of confidential data you need to protect, create a custom card to match your needs.</p>",
+ 'fields': [
+ {label:"Label 1", type:'TXT'},
+ {label:"Label 2", type:'TXT'},
+ {label:"Label 3", type:'TXT'}
+ ]
+ }
+},
+
+
+'recordFieldTypologies': {
+ 'TXT': {
+ description: "simple text field",
+ shortDescription: "text"
+ },
+ 'PWD': {
+ description: "simple text field, with default status set to hidden",
+ shortDescription: "password"
+ },
+ 'URL': {
+ description: "simple text field in edit mode, that became an active url in view mode",
+ shortDescription: "web address"
+ },
+ 'DATE': {
+ description: "a value set with a calendar helper",
+ shortDescription: "date"
+ },
+ 'ADDR': {
+ description: "just like the URL, but the active link points to Google Maps (or similar service) passing the address value as argument",
+ shortDescription: "street address"
+ },
+ 'CHECK': {
+ description: "check description",
+ shortDescription: "check"
+ },
+ 'RADIO': {
+ description: "radio description",
+ shortDescription: "radio"
+ },
+ 'SELECT': {
+ description: "select description",
+ shortDescription: "select"
+ }
+},
+
+// Cards block - new card - warnings
+'newRecordPanelGeneralExceptionTitle': "Error",
+'newRecordPanelGeneralExceptionMessage': "The configuration text is not valid. Make sure to get your text from the bookmarklet pop-up and retry.",
+'newRecordPanelWrongBookmarkletVersionExceptionTitle': "Error",
+'newRecordPanelWrongBookmarkletVersionExceptionMessage': "The configuration text has been generated by an old version of the bookmarklet. Please update your bookmarklet and retry.",
+'newRecordPanelExceptionPanelCloseButtonLabel': "Cancel",
+
+// Cards block - delete card
+'mainPanelDeletingRecordPanelConfirmationTitle': "Deleting selected card",
+'mainPanelDeleteRecordPanelConfirmationText': "Do your really want to delete the selected card?",
+'mainPanelDeleteRecordPanelConfirmButtonLabel': "Yes",
+'mainPanelDeleteRecordPanelDenyButtonLabel': "No",
+'mainPanelDeletingRecordPanelInitialTitle': "Deleting selected card",
+'mainPanelDeletingRecordPanelInitialText': "---",
+'mainPanelDeletingRecordPanelCompletedText': "Done",
+
+// Cards block - delete card panel
+'deleteRecordPanelCollectRecordDataMessageTitle': "Delete card",
+'deleteRecordPanelCollectRecordDataMessageText': "Updating card list",
+'deleteRecordPanelEncryptUserDataMessageTitle': "Delete card",
+'deleteRecordPanelEncryptUserDataMessageText': "Local encryption of card headers",
+'deleteRecordPanelSendingDataToTheServerMessageTitle': "Delete card",
+'deleteRecordPanelSendingDataToTheServerMessageText': "Uploading encrypted card headers to Clipperz",
+'deleteRecordPanelUpdatingTheInterfaceMessageTitle': "Delete card",
+'deleteRecordPanelUpdatingTheInterfaceMessageText': "Updating the interface",
+
+// Cards block - no record selected
+'recordDetailNoRecordSelectedTitle': "No card selected",
+'recordDetailNoRecordSelectedDescription': "<p>Please select a card from the list on the left.</p>",
+
+// Cards block - loading messages
+'recordDetailLoadingRecordMessage': "Downloading encrypted card from Clipperz",
+'recordDetailDecryptingRecordMessage': "Local decryption of card\'s data",
+'recordDetailLoadingRecordVersionMessage': "Downloading latest card version",
+'recordDetailDecryptingRecordVersionMessage': "Local decryption of latest version",
+'recordDetailLoadingErrorMessageTitle': "Error while downloading the card",
+
+// Cards block - card details
+'recordDetailNotesLabel': "Notes",
+'recordDetailLabelFieldColumnLabel': "Field label",
+'recordDetailDataFieldColumnLabel': "Field data",
+'recordDetailTypeFieldColumnLabel': "Type",
+
+'recordDetailSavingChangesMessagePanelInitialTitle': "Saving card",
+'recordDetailSavingChangesMessagePanelInitialText': "---",
+
+'recordDetailRemoveFieldButtonLabel': "-",
+'recordDetailAddFieldButtonLabel': "Add new field",
+'recordDetailPasswordFieldHelpLabel': "click the stars to select the password and then Ctrl-C to copy",
+
+'recordDetailPasswordFieldScrambleLabel': "scramble",
+'recordDetailPasswordFieldUnscrambleLabel': "unscramble",
+
+'recordDetailDirectLoginBlockTitle': "Direct logins",
+'recordDetailNewDirectLoginDescription': "<p>Direct login configuration</p>",
+
+'recordDetailDirectLoginBlockNoDirectLoginConfiguredDescription': "\
+ <p>Does this card contain credentials to access an online service?</p>\
+ <p>Use the bookmarklet to configure a “direct login” from Clipperz with just one click!</p>",
+
+'recordDetailDeleteDirectLoginButtonLabel': "-",
+'recordDetailAddNewDirectLoginButtonLabel': "Add new direct login",
+
+'recordDetailEditButtonLabel': "Edit",
+'recordDetailSaveButtonLabel': "Save",
+'recordDetailCancelButtonLabel': "Cancel",
+
+'newRecordTitleLabel': "_new card_",
+'newDirectLoginLabelSuffix': "",
+
+// Cards block - save card panel
+'recordSaveChangesPanelCollectRecordInfoMessageTitle': "Save card",
+'recordSaveChangesPanelCollectRecordInfoMessageText': "Updating card headers",
+'recordSaveChangesPanelEncryptUserDataMessageTitle': "Save card",
+'recordSaveChangesPanelEncryptUserDataMessageText': "Local encryption of card headers",
+'recordSaveChangesPanelEncryptRecordDataMessageTitle': "Save card",
+'recordSaveChangesPanelEncryptRecordDataMessageText': "Local encryption of card's data",
+'recordSaveChangesPanelEncryptRecordVersionDataMessageTitle': "Save card",
+'recordSaveChangesPanelEncryptRecordVersionDataMessageText': "Local encryption of card's version data",
+'recordSaveChangesPanelSendingDataToTheServerMessageTitle': "Save card",
+'recordSaveChangesPanelSendingDataToTheServerMessageText': "Uploading encrypted card's header to Clipperz",
+'recordSaveChangesPanelUpdatingTheInterfaceMessageTitle': "Save card",
+'recordSaveChangesPanelUpdatingTheInterfaceMessageText': "Updating the interface",
+
+// Password Generator strings
+'passwordGeneratorPanelTitle': "Password generator",
+'passwordGeneratorPanelOkLabel': "Ok",
+'passwordGeneratorPanelCancelLabel': "Cancel",
+
+'passwordGeneratorLowercaseLabel': "abc",
+'passwordGeneratorUppercaseLabel': "ABC",
+'passwordGeneratorNumberLabel': "012",
+'passwordGeneratorSymbolLabel': "@#$",
+
+'passwordGeneratorLengthLabel': "length:",
+
+
+//Miscellaneous strings
+
+'comingSoon': "coming soon …",
+'panelCollectingEntryopyMessageText': "Collecting entropy",
+'directLoginConfigurationCheckBoxFieldSelectedValue': "Yes",
+'directLoginConfigurationCheckBoxFieldNotSelectedValue': "No",
+
+
+
+// NEW - Import panel
+'importFormats': {
+ 'CSV': {
+ 'label': "CSV",
+ 'description': "<p>A widely recognized file format that stores tabular data. Several password managers can export data to this format.</p>"
+ },
+ 'Excel': {
+ 'label': "Excel",
+ 'description': "<p>The popular spreadsheet from Microsoft. Storing passwords in Excel files is very common but not advisable.</p>"
+ },
+ 'KeePass': {
+ 'label': "KeePass",
+ 'description': "<p>The custom TXT file created by KeePass password manager.</p>"
+ },
+ 'PasswordPlus': {
+ 'label': "Password Plus",
+ 'description': "<p>The custom CSV format produced by Password Plus, a password manager mostly used on mobile devices.</p>"
+ },
+ 'Roboform': {
+ 'label': "RoboForm",
+ 'description': "<p>The special HTML file created by Roboform password manager when displaying Passcard and Safenotes for printing.</p>"
+ },
+ 'ClipperzExport': {
+ 'label': "JSON",
+ 'description': "<p>The file created by Clipperz itself in JSON format. It preserves all information contained in your cards, even direct login configurations.</p>"
+ }
+},
+
+// JSON
+'Clipperz_ImportWizard_Title': "JSON import",
+'importOptions_clipperz_description': "<p>Open the JSON file exported from Clipperz in a text editor. Then copy and paste its content to the text area below.</p>",
+
+// CSV
+'CSV_ImportWizard_Title': "CSV import",
+'importOptions_csv_description_': "\
+ <p>Open the CSV file in a text editor. Then copy and paste its content to the text area below.</p>\
+ <p>Please select the special characters used within your file.</p>",
+
+// Excel
+'Excel_ImportWizard_Title': "Excel import",
+'importOptions_excel_description_': "<p>Open the Excel file and select the cells you want to import. Then copy and paste them to the text area below.</p>",
+
+// KeePass
+'KeePass_ImportWizard_Title': "KeePass import",
+'importOptions_keePass_description_': "<p>Open the TXT file created by Keepass in a text editor. Then copy and paste its content to the text area below.</p>",
+
+// PasswordPlus
+'PasswordPlus_ImportWizard_Title': "Password Plus import",
+'importOptions_passwordPlus_description': "<p>Open the CSV file created by PasswordPlus in a text editor. Then copy and paste its content to the text area below.</p>",
+
+// RoboForm
+'RoboForm_ImportWizard_Title': "RoboForm import",
+'importOptions_roboForm_description': "<p>Open the HTML file created by RoboForm in a text editor. Then copy and paste its content to the text area below.</p>",
+
+
+'importData_parsingDataTitle': "Import",
+'importData_parsingDataText': "Parsing data …",
+
+'importData_previewingDataTitle': "Import",
+'importData_previewingDataText': "Processing data …",
+
+'importData_processingDataTitle': "Import",
+'importData_processingDataText': "Creating new cards …",
+
+'ImportWizard': {
+ 'EDIT': "edit",
+ 'PREVIEW': "preview",
+ 'IMPORT': "import",
+
+ 'KEEPASS_SETTINGS': "settings",
+
+ 'CSV_EDIT': "paste",
+ 'CSV_COLUMNS': "columns",
+ 'CSV_HEADER': "labels",
+ 'CSV_TITLE': "titles",
+ 'CSV_NOTES': "notes",
+ 'CSV_FIELDS': "types",
+
+ 'EXCEL_EDIT': "edit"
+},
+
+'CSV_ImportWizard_Columns': "<p>Select the columns you want to import.</p>",
+'CSV_ImportWizard_Header': "<p>If the first row of the CSV file contains field labels, tick off the checkbox below.</p>",
+'CSV_ImportWizard_Header_Settings_firstRowHeaderLabel': "Use the first row as labels?",
+'CSV_ImportWizard_Title': "<p>Select the column that contains titles of the cards you are importing. (mandatory)</p>",
+'CSV_ImportWizard_Notes': "<p>Select the column that represents a \"notes\" field. (optional)</p>",
+'CSV_ImportWizard_Notes_Settings_noSelectionLabel': "\"notes\" field not present",
+'CSV_ImportWizard_Fields': "<p>Select the correct type for each column from the drop down lists.</p>",
+'CSV_ImportWizard_Fields_MissingLabelWarning': "Missing label",
+
+'importData_importConfirmation_title': "Import",
+'importData_importConfirmation_text': "Do you want to import __numberOfRecords__ cards?",
+
+
+// Vulnerability warning
+'VulnerabilityWarning_Panel_title': "Vulnerability warning",
+'VulnerabilityWarning_Panel_message': "The action as been aborted due to a catched vulnerability",
+'VulnerabilityWarning_Panel_buttonLabel': "Close",
+
+
+
+// All the loginInfo panel infos
+
+'WELCOME_BACK': "Welcome back!",
+
+'currentConnectionText': "You are connected from ip&nbsp;__ip__, apparently from __country__, using __browser__ on __operatingSystem__.",
+'latestConnectionText': "Your latest connection was __elapsedTimeDescription__ (__time__) from ip&nbsp;__ip__, apparently from __country__, using __browser__ on __operatingSystem__.",
+
+'fullLoginHistoryLinkLabel': "show login history",
+
+'elapsedTimeDescriptions': {
+ 'MORE_THAN_A_MONTH_AGO': "more than a month ago",
+ 'MORE_THAN_A_WEEK_AGO': "more than a week ago",
+ 'MORE_THAN_*_WEEKS_AGO': "more than __elapsed__ weeks ago",
+ 'YESTERDAY': "yesterday",
+ '*_DAYS_AGO': "__elapsed__ days ago",
+ 'ABOUT_AN_HOUR_AGO': "about an hour ago",
+ '*_HOURS_AGO': "__elapsed__ hours ago",
+ 'JUST_A_FEW_MINUTES_AGO': "just a few minutes ago",
+ 'ABOUT_*_MINUTES_AGO': "about __elapsed__ minutes ago"
+},
+
+'unknown_ip': "unknown",
+
+'countries': {
+ '--': "unknown",
+ 'AD': "Andorra",
+ 'AE': "United Arab Emirates",
+ 'AF': "Afghanistan",
+ 'AG': "Antigua and Barbuda",
+ 'AI': "Anguilla",
+ 'AL': "Albania",
+ 'AM': "Armenia",
+ 'AN': "Netherlands Antilles",
+ 'AO': "Angola",
+ 'AP': "Non-Spec Asia Pas Location",
+ 'AR': "Argentina",
+ 'AS': "American Samoa",
+ 'AT': "Austria",
+ 'AU': "Australia",
+ 'AW': "Aruba",
+ 'AX': "Aland Islands",
+ 'AZ': "Azerbaijan",
+ 'BA': "Bosnia and Herzegowina",
+ 'BB': "Barbados",
+ 'BD': "Bangladesh",
+ 'BE': "Belgium",
+ 'BF': "Burkina Faso",
+ 'BG': "Bulgaria",
+ 'BH': "Bahrain",
+ 'BI': "Burundi",
+ 'BJ': "Benin",
+ 'BM': "Bermuda",
+ 'BN': "Brunei Darussalam",
+ 'BO': "Bolivia",
+ 'BR': "Brazil",
+ 'BS': "Bahamas",
+ 'BT': "Bhutan",
+ 'BW': "Botswana",
+ 'BY': "Belarus",
+ 'BZ': "Belize",
+ 'CA': "Canada",
+ 'CD': "Congo the Democratic Republic of the",
+ 'CF': "Central African Republic",
+ 'CH': "Switzerland",
+ 'CI': "Cote D'ivoire",
+ 'CK': "Cook Islands",
+ 'CL': "Chile",
+ 'CM': "Cameroon",
+ 'CN': "China",
+ 'CO': "Colombia",
+ 'CR': "Costa Rica",
+ 'CS': "Serbia and Montenegro",
+ 'CU': "Cuba",
+ 'CY': "Cyprus",
+ 'CZ': "Czech Republic",
+ 'DE': "Germany",
+ 'DJ': "Djibouti",
+ 'DK': "Denmark",
+ 'DO': "Dominican Republic",
+ 'DZ': "Algeria",
+ 'EC': "Ecuador",
+ 'EE': "Estonia",
+ 'EG': "Egypt",
+ 'ER': "Eritrea",
+ 'ES': "Spain",
+ 'ET': "Ethiopia",
+ 'EU': "European Union",
+ 'FI': "Finland",
+ 'FJ': "Fiji",
+ 'FM': "Micronesia Federated States of",
+ 'FO': "Faroe Islands",
+ 'FR': "France",
+ 'GA': "Gabon",
+ 'GB': "United Kingdom",
+ 'GD': "Grenada",
+ 'GE': "Georgia",
+ 'GF': "French Guiana",
+ 'GG': "Guernsey",
+ 'GH': "Ghana",
+ 'GI': "Gibraltar",
+ 'GL': "Greenland",
+ 'GM': "Gambia",
+ 'GP': "Guadeloupe",
+ 'GR': "Greece",
+ 'GT': "Guatemala",
+ 'GU': "Guam",
+ 'GW': "Guinea-Bissau",
+ 'GY': "Guyana",
+ 'HK': "Hong Kong",
+ 'HN': "Honduras",
+ 'HR': "Croatia (Local Name: Hrvatska)",
+ 'HT': "Haiti",
+ 'HU': "Hungary",
+ 'ID': "Indonesia",
+ 'IE': "Ireland",
+ 'IL': "Israel",
+ 'IM': "Isle of Man",
+ 'IN': "India",
+ 'IO': "British Indian Ocean Territory",
+ 'IQ': "Iraq",
+ 'IR': "Iran (Islamic Republic of)",
+ 'IS': "Iceland",
+ 'IT': "Italy",
+ 'JE': "Jersey",
+ 'JM': "Jamaica",
+ 'JO': "Jordan",
+ 'JP': "Japan",
+ 'KE': "Kenya",
+ 'KG': "Kyrgyzstan",
+ 'KH': "Cambodia",
+ 'KI': "Kiribati",
+ 'KN': "Saint Kitts and Nevis",
+ 'KR': "Korea Republic of",
+ 'KW': "Kuwait",
+ 'KY': "Cayman Islands",
+ 'KZ': "Kazakhstan",
+ 'LA': "Lao People's Democratic Republic",
+ 'LB': "Lebanon",
+ 'LC': "Saint Lucia",
+ 'LI': "Liechtenstein",
+ 'LK': "Sri Lanka",
+ 'LR': "Liberia",
+ 'LS': "Lesotho",
+ 'LT': "Lithuania",
+ 'LU': "Luxembourg",
+ 'LV': "Latvia",
+ 'LY': "Libyan Arab Jamahiriya",
+ 'MA': "Morocco",
+ 'MC': "Monaco",
+ 'MD': "Moldova Republic of",
+ 'MG': "Madagascar",
+ 'MH': "Marshall Islands",
+ 'MK': "Macedonia the Former Yugoslav Republic of",
+ 'ML': "Mali",
+ 'MM': "Myanmar",
+ 'MN': "Mongolia",
+ 'MO': "Macau",
+ 'MP': "Northern Mariana Islands",
+ 'MR': "Mauritania",
+ 'MS': "Montserrat",
+ 'MT': "Malta",
+ 'MU': "Mauritius",
+ 'MV': "Maldives",
+ 'MW': "Malawi",
+ 'MX': "Mexico",
+ 'MY': "Malaysia",
+ 'MZ': "Mozambique",
+ 'NA': "Namibia",
+ 'NC': "New Caledonia",
+ 'NF': "Norfolk Island",
+ 'NG': "Nigeria",
+ 'NI': "Nicaragua",
+ 'NL': "Netherlands",
+ 'NO': "Norway",
+ 'NP': "Nepal",
+ 'NR': "Nauru",
+ 'NU': "Niue",
+ 'NZ': "New Zealand",
+ 'OM': "Oman",
+ 'PA': "Panama",
+ 'PE': "Peru",
+ 'PF': "French Polynesia",
+ 'PG': "Papua New Guinea",
+ 'PH': "Philippines",
+ 'PK': "Pakistan",
+ 'PL': "Poland",
+ 'PR': "Puerto Rico",
+ 'PS': "Palestinian Territory Occupied",
+ 'PT': "Portugal",
+ 'PW': "Palau",
+ 'PY': "Paraguay",
+ 'QA': "Qatar",
+ 'RO': "Romania",
+ 'RS': "Serbia",
+ 'RU': "Russian Federation",
+ 'RW': "Rwanda",
+ 'SA': "Saudi Arabia",
+ 'SB': "Solomon Islands",
+ 'SC': "Seychelles",
+ 'SD': "Sudan",
+ 'SE': "Sweden",
+ 'SG': "Singapore",
+ 'SI': "Slovenia",
+ 'SK': "Slovakia (Slovak Republic)",
+ 'SL': "Sierra Leone",
+ 'SM': "San Marino",
+ 'SN': "Senegal",
+ 'SR': "Suriname",
+ 'SV': "El Salvador",
+ 'SY': "Syrian Arab Republic",
+ 'SZ': "Swaziland",
+ 'TC': "Turks and Caicos Islands",
+ 'TG': "Togo",
+ 'TH': "Thailand",
+ 'TJ': "Tajikistan",
+ 'TM': "Turkmenistan",
+ 'TN': "Tunisia",
+ 'TO': "Tonga",
+ 'TR': "Turkey",
+ 'TT': "Trinidad and Tobago",
+ 'TV': "Tuvalu",
+ 'TW': "Taiwan Province of China",
+ 'TZ': "Tanzania United Republic of",
+ 'UA': "Ukraine",
+ 'UG': "Uganda",
+ 'US': "United States",
+ 'UY': "Uruguay",
+ 'UZ': "Uzbekistan",
+ 'VA': "Holy See (Vatican City State)",
+ 'VE': "Venezuela",
+ 'VG': "Virgin Islands (British)",
+ 'VI': "Virgin Islands (U.S.)",
+ 'VN': "Viet Nam",
+ 'VU': "Vanuatu",
+ 'WF': "Wallis and Futuna Islands",
+ 'WS': "Samoa",
+ 'YE': "Yemen",
+ 'ZA': "South Africa",
+ 'ZM': "Zambia",
+ 'ZW': "Zimbabwe",
+ 'ZZ': "Reserved"
+},
+
+'browsers': {
+ 'UNKNOWN': "Unknown",
+ 'MSIE': "Internet Explorer",
+ 'FIREFOX': "Firefox",
+ 'OPERA': "Opera",
+ 'SAFARI': "Safari",
+ 'OMNIWEB': "OmniWeb",
+ 'CAMINO': "Camino",
+ 'CHROME': "Chrome"
+},
+
+'operatingSystems': {
+ 'UNKNOWN': "Unknown",
+ 'WINDOWS': "Windows",
+ 'MAC': "Mac",
+ 'LINUX': "Linux",
+ 'IPHONE': "iPhone",
+ 'MOBILE': "Mobile",
+ 'OPENBSD': "OpenBSD",
+ 'FREEBSD': "FreeBSD",
+ 'NETBSD': "NetBSD"
+},
+
+
+// Calendar texts
+'calendarStrings': {
+ 'months': {
+ '0': "January",
+ '1': "February",
+ '2': "March",
+ '3': "April",
+ '4': "May",
+ '5': "June",
+ '6': "July",
+ '7': "August",
+ '8': "September",
+ '9': "October",
+ '10': "November",
+ '11': "December"
+ },
+ 'shortMonths': {
+ '0': "Jan",
+ '1': "Feb",
+ '2': "Mar",
+ '3': "Apr",
+ '4': "May",
+ '5': "Jun",
+ '6': "Jul",
+ '7': "Aug",
+ '8': "Sep",
+ '9': "Oct",
+ '10': "Nov",
+ '11': "Dec"
+ },
+
+ 'days': {
+ '0': "Sunday",
+ '1': "Monday",
+ '2': "Tuesday",
+ '3': "Wednesday",
+ '4': "Thursday",
+ '5': "Friday",
+ '6': "Saturday"
+ },
+
+ 'shortDays': {
+ '0': "Sun",
+ '1': "Mon",
+ '2': "Tue",
+ '3': "Wed",
+ '4': "Thu",
+ '5': "Fri",
+ '6': "Sat"
+ },
+
+ 'veryShortDays': {
+ '0': "Su",
+ '1': "Mo",
+ '2': "Tu",
+ '3': "We",
+ '4': "Th",
+ '5': "Fr",
+ '6': "Sa"
+ },
+
+ 'amDesignation': "am",
+ 'pmDesignation': "pm"
+
+},
+
+// Date format
+'fullDate_format': "l, F d, Y H:i:s",
+*/
+//################################################################################
+/*
+'pageHeader': {
+ 'donation': "donAte",
+ 'forum': "foRum",
+ 'credits': "creDits",
+ 'feedback': "feeDback",
+ 'help': "hElp"
+},
+
+
+'bookmarkletCopy': {
+ 'noExceptionMessage': "The direct login configuration has been collected.",
+ 'exceptionMessage': "Sorry! There was an error while processing the page.",
+ 'copy': "copy",
+ 'successfulMessage': "DONE!",
+ 'failMessage': "Failed! :("
+},
+*/
+//################################################################################
+
+'Wizards': {
+ 'DirectLoginWizard': {
+ 'LABEL': {
+ 'name': "label",
+ 'description': "Enter a name for your new direct login."
+ },
+ 'TYPE': {
+ 'name': "type",
+ 'description': "short description of the different types of direct login available"
+ },
+ 'CONFIGURATION': {
+ 'name': "config", // "bookmarklet config",
+ 'description': "Paste the code collected by the bookmarklet. (To install the bookmarklet drag the link below to the bookmark bar of your browser.)"
+ },
+ 'BINDINGS': {
+ 'name': "bindings",
+ 'description': "Select the right value for each field from the drop down menus."
+ },
+ 'FAVICON': {
+ 'name': "favicon",
+ 'description': "If you are not satisfied with the small icon for this direct login, enter the URL of a new image file (.ico, .png, .jpg)."
+ },
+ 'DONE': {
+ 'name': "done",
+ 'description': "Congratulations! You have created a new direct login using credentials contained in your '__cardName__' card.\nEnjoy 1-clik access to '__directLoginName__'!"
+ }
+ },
+ 'NewUserWizard': {
+ 'CREDENTIALS': {
+ 'name': "credentials",
+ 'description': "[choose you credentials]"
+ },
+ 'CHECK_CREDENTIALS': {
+ 'name': "check credentials",
+ 'description': "[check credentials]"
+ },
+ 'TERMS_OF_SERVICE': {
+ 'name': "terms of service",
+ 'description': "[terms of service]"
+ },
+ 'CREATE_USER': {
+ 'name': "login",
+ 'description': "[create user]"
+ } //,
+/*
+ 'LOGIN': {
+ 'name': "login",
+ 'description': "[enjoy Clipperz]"
+ },
+*/
+ }
+},
+
+
+'exceptionsMessages': {
+ 'Clipperz': {
+ 'Crypto': {
+ 'Base': {
+ 'exception': {
+ 'CorruptedMessage': "Corrupted message"
+ }
+ }
+ }
+ }
+},
+
+
+__syntaxFix__: "syntax fix"
+
+}
diff --git a/frontend/delta/js/Clipperz/PM/Toll.js b/frontend/delta/js/Clipperz/PM/Toll.js
new file mode 100644
index 0000000..e9c3092
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/Toll.js
@@ -0,0 +1,189 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
+if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
+
+//=============================================================================
+
+Clipperz.PM.Toll = function(args) {
+ args = args || {};
+
+ this._requestType = args.requestType;
+ this._targetValue = args.targetValue;
+ this._cost = args.cost;
+ this._toll = null;
+
+ return this;
+}
+
+Clipperz.PM.Toll.prototype = MochiKit.Base.update(null, {
+
+ 'toString': function() {
+ return "Clipperz.PM.Toll (" + this.requestType() + ": " + this.cost() + " - " + ((this.toll() == null)? 'UNPAID' : 'PAID') + ")";
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'requestType': function() {
+ return this._requestType;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'targetValue': function() {
+ return this._targetValue;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'cost': function() {
+ return this._cost;
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'toll': function() {
+ return this._toll;
+ },
+
+ //-------------------------------------------------------------------------
+/*
+ '__pay': function() {
+ var result;
+ var targetData;
+ var targetMatchSize;
+ var prefixMatchingBits;
+ var payment;
+ var i;
+
+ if (this.toll() == null) {
+ i = 0;
+ targetData = new Clipperz.ByteArray("0x" + this.targetValue());
+ targetMatchSize = this.cost();
+
+ payment = Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(32);
+
+ do {
+ var paymentData;
+
+ //payment = Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(32);
+ payment.increment();
+ paymentData = Clipperz.Crypto.SHA.sha256(payment);
+// prefixMatchingBits = this.prefixMatchingBits(targetData, paymentData);
+ prefixMatchingBits = Clipperz.ByteArray.prefixMatchingBits(targetData, paymentData);
+ i++;
+ } while (prefixMatchingBits < targetMatchSize);
+
+ this._toll = payment.toHexString().substring(2)
+ }
+
+ return this;
+ },
+*/
+ //-------------------------------------------------------------------------
+
+ 'innerDeferredPay': function (aTargetValue, aCost, aPayment) {
+ var deferredResult;
+ var result;
+ var payment;
+ var i;
+
+ result = null;
+ payment = aPayment;
+ i = 0;
+
+ while ((result == null) && (i < Clipperz.PM.Toll.numberOfCloseLoopIterations)) {
+ if (Clipperz.ByteArray.prefixMatchingBits(aTargetValue, Clipperz.Crypto.SHA.sha256(payment)) > aCost) {
+ result = payment;
+ } else {
+ payment.increment();
+ }
+
+ i ++;
+ }
+
+ if (result == null) {
+ deferredResult = MochiKit.Async.callLater(Clipperz.PM.Toll.pauseBetweenEachCloseLoop, MochiKit.Base.method(this, 'innerDeferredPay', aTargetValue, aCost, aPayment));
+ } else {
+ deferredResult = MochiKit.Async.succeed(result);
+ }
+
+ return deferredResult;
+ },
+
+ 'deferredPay': function () {
+ var deferredResult;
+ var toll;
+
+ toll = this;
+ deferredResult = new Clipperz.Async.Deferred("Toll.deferredPay");
+//deferredResult.addLog("--->>> deferredPay - " + this.cost());
+ deferredResult.addMethod(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'getRandomBytes', 32);
+ deferredResult.addMethod(toll, 'innerDeferredPay', new Clipperz.ByteArray("0x" + this.targetValue()), this.cost());
+ deferredResult.addCallback(MochiKit.Base.bind(function(aPayment) {
+ var result;
+
+ result = {
+ targetValue: this.targetValue(),
+ toll: aPayment.toHexString().substr(2)
+ };
+
+ return result;
+ }, this));
+//deferredResult.addLog("<<<--- deferredPay - " + this.cost());
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+
+});
+
+
+Clipperz.PM.Toll.validate = function(aTargetValue, aToll, aCost) {
+ var result;
+ var tollValue;
+ var targetValue;
+ var hashedTollValue;
+ var payedToll;
+
+ tollValue = new Clipperz.ByteArray("0x" + aToll);
+ targetValue = new Clipperz.ByteArray("0x" + aTargetValue);
+ hashedTollValue = Clipperz.Crypto.SHA.sha256(tollValue);
+
+ payedToll = Clipperz.ByteArray.prefixMatchingBits(targetValue, hashedTollValue);
+
+ if (payedToll < aCost) {
+ result = false;
+ } else {
+ result = true;
+ }
+
+ return result;
+};
+
+Clipperz.PM.Toll.numberOfCloseLoopIterations = 50;
+Clipperz.PM.Toll.pauseBetweenEachCloseLoop = 0.5; \ No newline at end of file
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js b/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js
new file mode 100644
index 0000000..df514a2
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js
@@ -0,0 +1,142 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.CardDetail = React.createClass({
+
+ getDefaultProps: function () {
+ return {
+// searchDelay: 0.3
+ }
+ },
+
+ propTypes: {
+ card: React.PropTypes.object.isRequired
+ },
+
+ getInitialState: function () {
+ return {
+// showSearch: false,
+// searchTimer: null,
+ starred: false
+ };
+ },
+
+ handleDirectLoginClick: function (aDirectLoginReference, anEvent) {
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'runDirectLogin', {record:this.props.card['reference'], directLogin:aDirectLoginReference});
+ },
+
+ //=========================================================================
+
+ normalizeFieldValue: function (aValue) {
+ var result = [];
+ var rows = aValue.split('\n');
+
+ for (var i = 0; i < rows.length; i++) {
+ if (i > 0) {
+ result.push(React.DOM.br());
+ }
+ result.push(rows[i].replace(/[\s]/g, '\u00A0'));
+ }
+
+ return result;
+ },
+
+ renderField: function (aField) {
+//console.log("FIELD", aField);
+ var actionLabel;
+
+ if (aField['actionType'] == 'URL') {
+ actionLabel = "go";
+ } else if (aField['actionType'] == 'PASSWORD') {
+ actionLabel = "locked";
+ } else if (aField['actionType'] == 'EMAIL') {
+ actionLabel = "email";
+ } else {
+ actionLabel = "";
+ }
+
+ return React.DOM.div({className:'listItem ' + aField['actionType']}, [
+ React.DOM.div({className:'fieldWrapper'}, [
+ React.DOM.div({className:'fieldInnerWrapper'}, [
+ React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aField['label'])),
+ React.DOM.div({className:'valueWrapper'}, React.DOM.span({className:'value ' + aField['actionType']}, this.normalizeFieldValue(aField['value'])))
+ ])
+ ]),
+ React.DOM.div({className:'actionWrapper'}, [
+ React.DOM.div({className:aField['actionType']}, actionLabel)
+ ])
+ ]);
+ },
+
+ renderDirectLogin: function (aDirectLogin) {
+//console.log("DIRECT LOGIN", aDirectLogin);
+ return React.DOM.div({className:'listItem', onClick:MochiKit.Base.method(this, 'handleDirectLoginClick', aDirectLogin['reference'])}, [
+ React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aDirectLogin['label'])),
+ React.DOM.div({className:'faviconWrapper'}, React.DOM.img({className:'favicon', src:aDirectLogin['favicon']})),
+ React.DOM.div({className:'directLoginLinkWrapper'}, React.DOM.span({className:'directLoginLink'}, "go"))
+ ]);
+ },
+
+ handleBackClick: function (anEvent) {
+ window.history.back();
+ },
+
+ handleStarClick: function (anEvent) {
+ this.setState({starred: !this.state['starred']});
+ },
+
+ //=========================================================================
+
+ render: function () {
+ var card = this.props.card;
+ var starredStatus = (this.state['starred'] ? "starred" : "unstarred");
+
+ if ((typeof(card['fields']) != 'undefined') && (card['notes'] != '')) {
+ card['fields'].push({ 'actionType': 'NOTES', 'isHidden': false, 'label': "notes", 'reference': "notes", 'value': card['notes'] })
+ }
+
+ return React.DOM.div({className:'cardDetail'}, [
+ React.DOM.div({className:'header'}, [
+ React.DOM.div({className:'titleWrapper'}, React.DOM.div({className:'title'}, card.title)),
+// React.DOM.div({className:'titleWrapper'}, React.DOM.div({className:'title'}, card.title + ' ' + card.title + ' ' + card.title + ' ' + card.title)),
+ React.DOM.div({className:'backWrapper'}, React.DOM.a({className:'button back', onClick:this.handleBackClick}, "back")),
+ React.DOM.div({className:'starWrapper'}, React.DOM.a({className:'star', onClick:this.handleStarClick}, starredStatus))
+ ]),
+ React.DOM.div({className:'content'}, [
+ card.fields ? React.DOM.div({className:'fields'}, MochiKit.Base.map(this.renderField, card.fields)) : null,
+ card.directLogins ? React.DOM.div({className:'directLogins'}, MochiKit.Base.map(this.renderDirectLogin, card.directLogins)): null
+ ]),
+ React.DOM.div({className:'footer'}, [
+/*
+// React.DOM.a({className:'cancel'}, "cancel"),
+// React.DOM.a({className:'save'}, "save")
+
+ React.DOM.a({className:'cancel button'}, "failed"),
+ React.DOM.a({className:'save button'}, "done")
+*/
+ ])
+ ]);
+ }
+
+ //=========================================================================
+});
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js b/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js
new file mode 100644
index 0000000..66d20f1
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js
@@ -0,0 +1,161 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.CardList = React.createClass({
+
+ getDefaultProps: function () {
+ return {
+ selectedCard: null,
+ searchDelay: 0.3
+ }
+ },
+
+ propTypes: {
+ searchDelay: React.PropTypes.number
+ },
+
+ getInitialState: function () {
+ return {
+ showSearch: false,
+ searchTimer: null,
+ searchText: '',
+// passphrase: '',
+// pin: ''
+ };
+ },
+
+ //=========================================================================
+
+ toggleSearch: function (anEvent) {
+ var showSearchBox;
+
+ showSearchBox = !this.state.showSearch;
+
+ this.setState({showSearch: showSearchBox});
+
+ if (showSearchBox) {
+ MochiKit.Async.callLater(0.1, MochiKit.Base.method(this, 'focusOnSearchField'));
+ }
+ },
+
+ updateSearchText: function (anEvent) {
+ var searchText;
+
+ searchText = anEvent.target.value;
+//console.log(">>> updateSearchText", searchText);
+
+ if ((this.state['searchTimer'] != null) && (searchText != this.state['searchText'])) {
+ this.state['searchTimer'].cancel();
+ }
+
+ if (searchText != this.state['searchText']) {
+ this.state['searchText'] = searchText;
+ this.state['searchTimer'] = MochiKit.Async.callLater(this.props['searchDelay'], MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'searchCards', searchText);
+ }
+ },
+
+ focusOnSearchField: function () {
+console.log("focusOnSearchField", this.refs['searchField']);
+ this.refs['searchField'].getDOMNode.focus();
+ },
+
+ searchBox: function () {
+ var result;
+
+ if (this.state.showSearch) {
+ result = React.DOM.div({className:'searchBox'}, [
+ React.DOM.div(null, [
+ React.DOM.input({type:'search', placeholder:"search", ref:'searchField', onChange:this.updateSearchText})
+ ])
+ ]);
+ } else {
+ result = null;
+ }
+
+ return result;
+ },
+
+ //=========================================================================
+
+ cardItem: function (aRecordReference) {
+ var reference = aRecordReference['_reference'];
+ var selectedCard = (reference == this.props.selectedCard);
+
+ return React.DOM.div({className:'listItem', onClick:MochiKit.Base.method(this, 'handleClickOnCardDetail', reference)}, [
+ React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aRecordReference.label)),
+// React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label)),
+ React.DOM.div({className:'faviconWrapper'}, aRecordReference.favicon ? React.DOM.img({className:'favicon', src:aRecordReference.favicon}) : React.DOM.div({className:'favicon'}, '\u00A0')),
+ React.DOM.div({className:'detailLinkWrapper'}, React.DOM.span({className:'detailLink ' + (selectedCard ? 'icon-spin' : '')}, (selectedCard ? "loading" : "detail")))
+ ]);
+ },
+
+ handleClickOnCardDetail: function (aRecordReference, anEvent) {
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showRecord', aRecordReference);
+ },
+
+ cardListItems: function () {
+ var list;
+ var result;
+
+ list = this.props['cardList'];
+
+ if (typeof(list) != 'undefined') {
+ result = MochiKit.Base.map(MochiKit.Base.method(this, 'cardItem'), list);
+ } else {
+ result = null;
+ }
+
+ return result;
+ },
+
+ //=========================================================================
+
+ handleChange: function (anEvent) {
+// var refs = this.refs;
+// var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
+// var newState = {};
+//
+// newState[refName] = event.target.value;
+// this.setState(newState);
+ },
+
+ //=========================================================================
+
+ render: function() {
+ return React.DOM.div(null, [
+ React.DOM.div({className:'header'}, [
+ React.DOM.a({className:'account'}, 'clipperz'),
+ React.DOM.div({className:'features'}, [
+ React.DOM.a({className:'addCard'}, 'add'),
+ React.DOM.a({className:'search ' + (this.state.showSearch ? 'selected' : ''), onClick:this.toggleSearch}, 'search'),
+ React.DOM.a({className:'settings'}, 'settings')
+ ]),
+// this.searchBox()
+ ]),
+ this.searchBox(),
+ React.DOM.div({className:'content cardList'}, this.cardListItems()),
+ ]);
+ }
+
+ //=========================================================================
+});
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/ErrorPage.js b/frontend/delta/js/Clipperz/PM/UI/Components/ErrorPage.js
new file mode 100644
index 0000000..a1979ec
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/ErrorPage.js
@@ -0,0 +1,46 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.ErrorPage = React.createClass({
+
+ getDefaultProps: function () {
+ return {
+ template: Clipperz.PM.UI.Components.PageTemplate
+ }
+ },
+
+ 'propTypes': {
+// type: React.PropTypes.oneOf(['PERMANENT', 'TEMPORARY']),
+ message: React.PropTypes.string.isRequired,
+ template: React.PropTypes.func
+ },
+
+
+ _render: function () {
+ return React.DOM.div({className:'error-message'}, this.props.message);
+ },
+
+ render: function () {
+ return new this.props.template({'innerComponent': this._render()});
+ }
+});
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js b/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js
new file mode 100644
index 0000000..2b5b4a4
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js
@@ -0,0 +1,150 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.LoginForm = React.createClass({
+
+ getDefaultProps: function () {
+ return {
+ mode: 'CREDENTIALS',
+ isNewUserRegistrationAvailable: false,
+ disabled: false,
+ template: Clipperz.PM.UI.Components.PageTemplate
+ }
+ },
+
+ propTypes: {
+ mode: React.PropTypes.oneOf(['CREDENTIALS','PIN']),
+ isNewUserRegistrationAvailable: React.PropTypes.bool,
+ disabled: React.PropTypes.bool,
+ template: React.PropTypes.func
+ },
+
+ getInitialState: function () {
+ return {
+ username: '',
+ passphrase: '',
+ pin: ''
+ };
+ },
+
+ //=========================================================================
+
+ handleChange: function (anEvent) {
+ var refs = this.refs;
+ var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
+ var newState = {};
+
+ newState[refName] = event.target.value;
+ this.setState(newState);
+ },
+
+ //=========================================================================
+
+ handleCredentialSubmit: function (event) {
+ event.preventDefault();
+
+ this.refs['passphrase'].getDOMNode().blur();
+
+ var credentials = {
+ 'username': this.refs['username'].getDOMNode().value,
+ 'passphrase': this.refs['passphrase'].getDOMNode().value
+ }
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', credentials);
+ },
+
+ handleRegistrationLinkClick: function (event) {
+ event.preventDefault();
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showRegistrationForm');
+ },
+
+ //-------------------------------------------------------------------------
+
+ shouldEnableLoginButton: function () {
+ var result;
+
+ return (
+ ((this.state['username'] != '') && (this.state['passphrase'] != ''))
+ ||
+ (this.state['pin'] != '')
+ ) && !this.props['disabled'];
+ },
+
+
+ loginForm: function () {
+ registrationLink = React.DOM.div({'className':'registrationLink'}, [
+ React.DOM.a({'onClick':this.handleRegistrationLinkClick}, "Need an account")
+ ]);
+ return React.DOM.div({'className':'loginForm credentials'},[
+ React.DOM.form({onChange: this.handleChange, onSubmit:this.handleCredentialSubmit}, [
+ React.DOM.div(null,[
+ React.DOM.label({'for':'name'}, "username"),
+ React.DOM.input({'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'key':'username', 'autoCapitalize':'none'}),
+ React.DOM.label({'for':'passphrase'}, "passphrase"),
+ React.DOM.input({'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase", 'key':'passphrase'})
+ ]),
+ React.DOM.button({'type':'submit', 'disabled':!this.shouldEnableLoginButton(), 'className':'button'}, "login")
+ ]),
+ this.props.isNewUserRegistrationAvailable ? registrationLink : null
+ ]);
+ },
+
+ handlePINSubmit: function (event) {
+ event.preventDefault();
+
+ this.refs['pin'].getDOMNode().blur();
+
+ var credentials = {
+ pin: this.refs['pin'].getDOMNode().value
+ }
+
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', credentials);
+ },
+
+ pinForm: function () {
+ return React.DOM.div({'className':'loginForm pin'},[
+ React.DOM.form({onChange: this.handleChange, onSubmit:this.handlePINSubmit}, [
+ React.DOM.div(null,[
+ React.DOM.label({'for':'pin'}, "pin"),
+ React.DOM.input({'type':'text', 'name':'pin', 'ref':'pin', placeholder:"PIN", 'key':'pin', 'autocapitalize':'none'})
+ ]),
+ React.DOM.button({'type':'submit', 'disabled':this.props.disabled, 'className':'button'}, "login")
+ ])
+ ]);
+ },
+
+ setInitialFocus: function () {
+ if (this.props.mode == 'PIN') {
+ this.refs['pin'].getDOMNode().select();
+ } else {
+ if (this.refs['username'].getDOMNode().value == '') {
+ this.refs['username'].getDOMNode().focus();
+ } else{
+ this.refs['passphrase'].getDOMNode().select();
+ }
+ }
+ },
+
+ render: function() {
+ return new this.props.template({'innerComponent': this.props.mode == 'PIN' ? this.pinForm() : this.loginForm()});
+ }
+});
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js b/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js
new file mode 100644
index 0000000..cc4a06c
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js
@@ -0,0 +1,122 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.Base.module('Clipperz.PM.UI.Components');
+
+Clipperz.PM.UI.Components.Overlay = function(args) {
+ args = args || {};
+
+ this._defaultDelay = 2;
+ this._element = MochiKit.DOM.getElement('overlay');
+
+ return this;
+}
+
+//=============================================================================
+
+Clipperz.Base.extend(Clipperz.PM.UI.Components.Overlay, Object, {
+
+ //-------------------------------------------------------------------------
+
+ 'toString': function () {
+ return "Clipperz.PM.UI.Components.Overlay component";
+ },
+
+ 'element': function () {
+// return MochiKit.DOM.getElement('overlay');
+ return this._element;
+ },
+
+ 'getElement': function (aClass) {
+ return MochiKit.Selector.findChildElements(this.element(), ['.'+aClass])[0];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'show': function (aMessage) {
+ this.resetStatus();
+ this.setMessage(aMessage);
+ MochiKit.DOM.removeElementClass(this.element(), 'ios-overlay-hide');
+ MochiKit.DOM.addElementClass(this.element(), 'ios-overlay-show');
+ },
+
+ 'done': function (aMessage, aDelayBeforeHiding) {
+ this.completed(this.showDoneIcon, aMessage, aDelayBeforeHiding);
+ },
+
+ 'failed': function (aMessage, aDelayBeforeHiding) {
+ this.completed(this.showFailIcon, aMessage, aDelayBeforeHiding);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'resetStatus': function () {
+ MochiKit.Style.showElement(this.element());
+ MochiKit.Style.showElement(this.getElement('spinner'));
+ MochiKit.Style.hideElement(this.getElement('done'));
+ MochiKit.Style.hideElement(this.getElement('failed'));
+ },
+
+ 'setMessage': function (aMessage) {
+ if (typeof(aMessage) != 'undefined') {
+ this.getElement('title').innerHTML = aMessage;
+ }
+ },
+
+ 'completed': function (aFunctionToShowResult, aMessage, aDelayBeforeHiding) {
+ var delay = aDelayBeforeHiding || this.defaultDelay();
+
+ this.hideSpinner();
+ MochiKit.Base.bind(aFunctionToShowResult, this)();
+ this.setMessage(aMessage);
+
+ MochiKit.Async.callLater(delay, MochiKit.Base.bind(this.hide, this))
+ },
+
+ 'hide': function () {
+ MochiKit.DOM.removeElementClass(this.element(), 'ios-overlay-show');
+ MochiKit.DOM.addElementClass(this.element(), 'ios-overlay-hide');
+ MochiKit.Async.callLater(1, MochiKit.Style.hideElement, this.element());
+ },
+
+ 'hideSpinner': function () {
+ MochiKit.Style.hideElement(this.getElement('spinner'));
+ },
+
+ 'showDoneIcon': function () {
+ MochiKit.Style.showElement(this.getElement('done'));
+ },
+
+ 'showFailIcon': function () {
+ MochiKit.Style.showElement(this.getElement('failed'));
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'defaultDelay': function () {
+ return this._defaultDelay;
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/PageTemplate.js b/frontend/delta/js/Clipperz/PM/UI/Components/PageTemplate.js
new file mode 100644
index 0000000..9b7c748
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/PageTemplate.js
@@ -0,0 +1,33 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.PageTemplate = React.createClass({
+ render: function() {
+ return React.DOM.div(null, [
+ React.DOM.div({'className': 'header'}, [
+ React.DOM.h1(null, "clipperz")
+ ]),
+ React.DOM.div({'className': 'content'}, this.props.innerComponent)
+ ])
+ }
+});
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/RegistrationWizard.js b/frontend/delta/js/Clipperz/PM/UI/Components/RegistrationWizard.js
new file mode 100644
index 0000000..051dcc5
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/RegistrationWizard.js
@@ -0,0 +1,240 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.RegistrationWizard = React.createClass({
+
+ getDefaultProps: function () {
+ return {
+ steps: [
+ {name:'CREDENTIALS', label:'registration', _label:'credentials', description:"Choose your credentails"},
+ {name:'PASSWORD_VERIFICATION', label:'registration', _label:'verify', description:"Verify your passphrase"},
+ {name:'TERMS_OF_SERVICE', label:'registration', _label:'terms', description:"Check our terms of service"}
+ ],
+ disabled: false,
+ template: Clipperz.PM.UI.Components.PageTemplate
+ }
+ },
+
+ getInitialState: function () {
+ return {
+ currentStep: this.props['steps'][0]['name'],
+ username: '',
+ passphrase: '',
+ verify_passphrase: '',
+ no_password_recovery: false,
+ agree_terms_of_service: false
+ };
+ },
+
+ 'propTypes': {
+// steps: React.PropTypes.array,
+ disabled: React.PropTypes.bool,
+ template: React.PropTypes.func
+ },
+
+ //=========================================================================
+
+ currentStepIndex: function () {
+ return this.indexOfStepNamed(this.state['currentStep']);
+ },
+
+ indexOfStepNamed: function (aStepName) {
+ var stepConfiguration;
+ var result;
+
+ stepConfiguration = this.props['steps'].filter(function (aConfig) { return aConfig['name'] == aStepName})[0];
+ result = this.props['steps'].indexOf(stepConfiguration);
+ return result;
+ },
+
+ //=========================================================================
+
+ statusClassForStep: function (aStep) {
+ var currentStepIndex = this.currentStepIndex();
+ var stepIndex = this.indexOfStepNamed(aStep['name']);
+ var result;
+
+ if (stepIndex < currentStepIndex) {
+ result = 'left';
+ } else if (stepIndex == currentStepIndex) {
+ result = 'center';
+ } else {
+ result = 'right';
+ }
+
+ return result;
+ },
+
+ //=========================================================================
+
+ handleBackClick: function (anEvent) {
+ var nextStep;
+ anEvent.preventDefault();
+
+ if (this.currentStepIndex() > 0) {
+ nextStep = this.props['steps'][this.currentStepIndex() - 1];
+ this.setState({currentStep: nextStep['name']});
+ } else {
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'goBack');
+ }
+ },
+
+ handleForwardClick: function (anEvent) {
+ var nextStep;
+ anEvent.preventDefault();
+
+ if (this.canMoveForward()) {
+
+ if (this.currentStepIndex() < this.props['steps'].length - 1) {
+ nextStep = this.props['steps'][this.currentStepIndex() + 1];
+ this.setState({currentStep: nextStep['name']});
+ } else {
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'registerNewUser', {
+ username: this.state['username'],
+ passphrase: this.state['passphrase']
+ })
+ }
+ }
+ },
+
+ //-------------------------------------------------------------------------
+
+ canMoveForward: function () {
+ var result;
+ var currentStep;
+
+ result = false;
+ currentStep = this.state['currentStep'];
+ if (currentStep == 'CREDENTIALS') {
+ result = ((this.state['username'] != '') && (this.state['passphrase'] != ''));
+ } else if (currentStep == 'PASSWORD_VERIFICATION') {
+ result = (this.state['passphrase'] == this.state['verify_passphrase']);
+ } else if (currentStep == 'TERMS_OF_SERVICE') {
+ result = (this.state['no_password_recovery'] && this.state['agree_terms_of_service']);
+ }
+
+ return result && !this.props['disabled'];
+ },
+
+ //=========================================================================
+
+ handleChange: function (anEvent) {
+ var refs = this.refs;
+ var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
+ var newState = {};
+
+ if ((event.target.type == 'checkbox') || (event.target.type == 'radio')) {
+ newState[refName] = event.target.checked;
+ } else {
+ newState[refName] = event.target.value;
+ }
+ this.setState(newState);
+ },
+
+ //=========================================================================
+
+ renderIndexStep: function (aStep) {
+ return React.DOM.div({'className':'stepIndexItem ' + this.statusClassForStep(aStep)}, '.');
+ },
+
+ renderButtons: function () {
+ return [
+ React.DOM.a({className:'back button step_' + (this.currentStepIndex() - 1), onClick:this.handleBackClick}, '<<'),
+ React.DOM.a({className:'forward button step_' + (this.currentStepIndex() + 1) + ' ' + (this.canMoveForward() ? 'enabled' : 'disabled'), onClick:this.handleForwardClick}, '>>')
+ ];
+ },
+
+ render_CREDENTIALS: function () {
+ return React.DOM.div(null,[
+ React.DOM.label({'for':'name'}, "username"),
+ React.DOM.input({'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'key':'username', 'autoCapitalize':'none'/*, value:this.state.username*/}),
+ React.DOM.label({'for':'passphrase'}, "passphrase"),
+ React.DOM.input({'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase", 'key':'passphrase'/*, value:this.state.passphrase*/})
+ ]);
+ },
+
+ render_PASSWORD_VERIFICATION: function () {
+ return React.DOM.div(null,[
+ React.DOM.label({'for':'verify_passphrase'}, "passphrase"),
+ React.DOM.input({'type':'password', 'name':'verify_passphrase', 'ref':'verify_passphrase', 'placeholder':"verify passphrase", 'key':'verify_passphrase'})
+ ]);
+ },
+
+ render_TERMS_OF_SERVICE: function () {
+ return React.DOM.div(null, [
+ React.DOM.div({className:'checkboxBlock'}, [
+ React.DOM.label({'for':'no_password_recovery'}, "I understand that Clipperz will not be able to recover a lost passphrase."),
+ React.DOM.input({'type':'checkbox', 'name':'no_password_recovery', 'ref':'no_password_recovery', 'key':'no_password_recovery'}),
+ React.DOM.p(null, "I understand that Clipperz will not be able to recover a lost passphrase.")
+ ]),
+ React.DOM.div({className:'checkboxBlock'}, [
+ React.DOM.label({'for':'agree_terms_of_service'}, "I have read and agreed to the Terms of Service."),
+ React.DOM.input({'type':'checkbox', 'name':'agree_terms_of_service', 'ref':'agree_terms_of_service', 'key':'agree_terms_of_service'}),
+ React.DOM.p(null, [
+ "I have read and agreed to the ",
+ React.DOM.a({href:'https://clipperz.com/terms_service/', target:'_blank'}, "Terms of Service.")
+ ])
+ ])
+ ]);
+ },
+
+ renderStep: function (aStep) {
+ return React.DOM.div({'className':'step' + ' ' + aStep['name'] + ' ' + this.statusClassForStep(aStep) + ' step_' + this.currentStepIndex()}, [
+ React.DOM.h1(null, aStep['label']),
+ React.DOM.p(null, aStep['description']),
+ this['render_' + aStep['name']].apply(),
+ React.DOM.div({'className':'stepIndex'}, MochiKit.Base.map(this.renderIndexStep, this.props['steps'])),
+ React.DOM.div({'className':'buttons'}, this.renderButtons())
+ ]);
+ },
+
+ _render: function () {
+ return React.DOM.div({'className':'registrationForm'},[
+ React.DOM.form({onChange: this.handleChange}, [
+ React.DOM.div({'className':'steps'}, MochiKit.Base.map(this.renderStep, this.props['steps']))
+ ])
+ ]);
+ },
+
+ render: function () {
+ return new this.props.template({'innerComponent': this._render()});
+ },
+
+ //=========================================================================
+
+ setInitialFocus: function () {
+ this.refs['username'].getDOMNode().focus();
+ },
+
+ componentDidUpdate: function (prevProps, prevState, rootNode) {
+ if (prevState['currentStep'] != this.state['currentStep']) {
+ if (this.state['currentStep'] == 'CREDENTIALS') {
+ this.refs['passphrase'].getDOMNode().select();
+ } else if (this.state['currentStep'] == 'PASSWORD_VERIFICATION') {
+ this.refs['verify_passphrase'].getDOMNode().select();
+ }
+ }
+ }
+
+ //=========================================================================
+});
diff --git a/frontend/delta/js/Clipperz/PM/UI/DirectLoginController.js b/frontend/delta/js/Clipperz/PM/UI/DirectLoginController.js
new file mode 100644
index 0000000..d9dfe6d
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/DirectLoginController.js
@@ -0,0 +1,256 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.Base.module('Clipperz.PM.UI');
+
+Clipperz.PM.UI.DirectLoginRunner = function(args) {
+ this._directLogin = args['directLogin'] || Clipperz.Base.exception.raise('MandatoryParameter');
+ this._target = Clipperz.PM.Crypto.randomKey();
+
+ return this;
+}
+
+MochiKit.Base.update(Clipperz.PM.UI.DirectLoginRunner.prototype, {
+
+ 'toString': function() {
+ return "Clipperz.PM.UI.DirectLoginRunner";
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'directLogin': function () {
+ return this._directLogin;
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'target': function () {
+ return this._target;
+ },
+
+ //=============================================================================
+
+ 'setWindowTitle': function (aWindow, aTitle) {
+ aWindow.document.title = aTitle;
+ },
+
+ 'setWindowBody': function (aWindow, anHTML) {
+ aWindow.document.body.innerHTML = anHTML;
+ },
+
+ //=============================================================================
+
+ 'initialWindowSetup': function (aWindow) {
+ this.setWindowTitle(aWindow, "Loading Clipperz Direct Login");
+ this.setWindowBody (aWindow, MochiKit.DOM.toHTML(MochiKit.DOM.H3("Loading Clipperz Direct Login ...")));
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'updateWindowWithDirectLoginLabel': function (aWindow, aLabel) {
+ var titleText;
+ var bodyText;
+
+ titleText = "Loading '__label__' Direct Login".replace(/__label__/, aLabel)
+ bodyText = "Loading '__label__' Direct Login... ".replace(/__label__/, aLabel)
+
+ this.setWindowTitle(aWindow, titleText);
+ this.setWindowBody (aWindow, MochiKit.DOM.toHTML(MochiKit.DOM.H3(bodyText)));
+ },
+
+ //-----------------------------------------------------------------------------
+
+ 'updateWindowWithHTMLContent': function (aWindow, anHtml) {
+ this.setWindowBody(aWindow, anHtml);
+ },
+
+ //=============================================================================
+
+ 'submitLoginForm': function(aWindow, aSubmitFunction) {
+ MochiKit.DOM.withWindow(aWindow, MochiKit.Base.bind(function () {
+ var formElement;
+ var submitButtons;
+
+ formElement = MochiKit.DOM.getElement('directLoginForm');
+
+ submitButtons = MochiKit.Base.filter(function(anInputElement) {
+ return ((anInputElement.tagName.toLowerCase() == 'input') && (anInputElement.getAttribute('type').toLowerCase() == 'submit'));
+ }, formElement.elements);
+
+ if (submitButtons.length == 0) {
+ if (typeof(formElement.submit) == 'function') {
+ formElement.submit();
+ } else {
+ aSubmitFunction.apply(formElement);
+ }
+/*
+ var formSubmitFunction;
+
+ formSubmitFunction = MochiKit.Base.method(formElement, 'submit');
+ if (Clipperz_IEisBroken == true) {
+ formElement.submit();
+ } else {
+ formSubmitFunction();
+ }
+*/
+ } else {
+ submitButtons[0].click();
+ }
+ }, this));
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'runSubmitFormDirectLogin': function (aWindow, someAttributes) {
+ var html;
+ var formElement;
+ var submitFunction;
+
+ formElement = MochiKit.DOM.FORM({
+ 'id':'directLoginForm',
+ 'method':someAttributes['formAttributes']['method'],
+ 'action':someAttributes['formAttributes']['action']
+ });
+
+ submitFunction = formElement.submit;
+
+ MochiKit.DOM.appendChildNodes(formElement, MochiKit.Base.map(function (anInputAttributes) {
+ return MochiKit.DOM.INPUT({'type':'hidden', 'name':anInputAttributes[0], 'value':anInputAttributes[1]});
+ }, MochiKit.Base.items(someAttributes['inputValues'])));
+
+ html = '';
+ html += '<h3>Loading ' + someAttributes['label'] + ' ...</h3>';
+ html += MochiKit.DOM.appendChildNodes(MochiKit.DOM.DIV(), MochiKit.DOM.appendChildNodes(MochiKit.DOM.DIV({style:'display:none; visibility:hidden;'}), formElement)).innerHTML;
+
+ this.updateWindowWithHTMLContent(aWindow, html);
+ this.submitLoginForm(aWindow, submitFunction);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'runHttpAuthDirectLogin': function(aWindow, someAttributes) {
+ var completeUrl;
+ var url;
+
+ url = someAttributes['inputValues']['url'];
+
+ if (/^https?\:\/\//.test(url) == false) {
+ url = 'http://' + url;
+ }
+
+ if (Clipperz_IEisBroken === true) {
+ completeUrl = url;
+ } else {
+ var username;
+ var password;
+
+ username = someAttributes['inputValues']['username'];
+ password = someAttributes['inputValues']['password'];
+ /(^https?\:\/\/)?(.*)/.test(url);
+
+ completeUrl = RegExp.$1 + username + ':' + password + '@' + RegExp.$2;
+ }
+
+ window.open(completeUrl, this.target());
+ },
+
+ //=============================================================================
+
+ 'runDirectLogin': function (aWindow) {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred("DirectLoginRunner.openDirectLogin", {trace:false});
+ deferredResult.addMethod(this, 'initialWindowSetup', aWindow);
+ deferredResult.addMethod(this.directLogin(), 'label');
+ deferredResult.addMethod(this, 'updateWindowWithDirectLoginLabel', aWindow);
+ deferredResult.collectResults({
+ 'type': MochiKit.Base.method(this.directLogin(), 'type'),
+ 'label': MochiKit.Base.method(this.directLogin(), 'label'),
+ 'formAttributes': MochiKit.Base.method(this.directLogin(), 'formAttributes'),
+ 'inputValues': MochiKit.Base.method(this.directLogin(), 'inputValues')
+ });
+ deferredResult.addCallback(MochiKit.Base.bind(function (someAttributes) {
+ switch (someAttributes['type']) {
+ case 'http_auth':
+ this.runHttpAuthDirectLogin(aWindow, someAttributes);
+ break;
+ case 'simple_url':
+ this.runSimpleUrlDirectLogin(aWindow, someAttributes);
+ break;
+ default:
+ this.runSubmitFormDirectLogin(aWindow, someAttributes);
+ break;
+ }
+ }, this));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //=============================================================================
+
+ 'run': function () {
+ var newWindow;
+
+ newWindow = window.open(Clipperz.PM.Strings.getValue('directLoginJumpPageUrl'), this.target());
+
+ return this.runDirectLogin(newWindow);
+ },
+
+ //=============================================================================
+
+ 'test': function () {
+ var iFrame;
+ var newWindow;
+
+ iFrame = MochiKit.DOM.createDOM('iframe');
+ MochiKit.DOM.appendChildNodes(MochiKit.DOM.currentDocument().body, iFrame);
+
+ newWindow = iFrame.contentWindow;
+
+ return this.runDirectLogin(newWindow);
+ },
+
+ //=============================================================================
+ __syntaxFix__: "syntax fix"
+});
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.UI.DirectLoginRunner.openDirectLogin = function (aDirectLogin) {
+ var runner;
+
+ runner = new Clipperz.PM.UI.DirectLoginRunner({directLogin:aDirectLogin});
+ return runner.run();
+};
+
+//-----------------------------------------------------------------------------
+
+Clipperz.PM.UI.DirectLoginRunner.testDirectLogin = function (aDirectLogin) {
+ var runner;
+
+ runner = new Clipperz.PM.UI.DirectLoginRunner({directLogin:aDirectLogin});
+ return runner.test();
+};
+
+//-----------------------------------------------------------------------------
diff --git a/frontend/delta/js/Clipperz/PM/UI/MainController.js b/frontend/delta/js/Clipperz/PM/UI/MainController.js
new file mode 100644
index 0000000..da7540e
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/MainController.js
@@ -0,0 +1,491 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.Base.module('Clipperz.PM.UI');
+
+Clipperz.PM.UI.MainController = function() {
+ var pages;
+
+ this._proxy = null;
+ this._user = null;
+ this._filter = '';
+
+// this._currentPage = 'loadingPage';
+
+ this._pageStack = ['loadingPage'];
+ this._overlay = new Clipperz.PM.UI.Components.Overlay();
+ pages = {
+ 'loginPage': new Clipperz.PM.UI.Components.LoginForm(),
+ 'registrationPage': new Clipperz.PM.UI.Components.RegistrationWizard(),
+ 'cardListPage': new Clipperz.PM.UI.Components.CardList(),
+ 'cardDetailPage': new Clipperz.PM.UI.Components.CardDetail({card: {}}),
+ 'errorPage': new Clipperz.PM.UI.Components.ErrorPage({message:''})
+ };
+
+ MochiKit.Base.map(function (anId) {React.renderComponent(pages[anId], MochiKit.DOM.getElement(anId))}, MochiKit.Base.keys(pages));
+ this._pages = pages;
+ this.registerForNotificationCenterEvents();
+
+ return this;
+}
+
+MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, {
+
+ toString: function () {
+ return "Clipperz.PM.UI.MainController";
+ },
+
+ //=========================================================================
+
+ overlay: function () {
+ return this._overlay;
+ },
+
+ loginForm: function () {
+ return this._loginForm;
+ },
+
+ registrationWizard: function () {
+ return this._registrationWizard;
+ },
+
+ //=========================================================================
+
+ isOnline: function() {
+ return navigator.onLine;
+ },
+
+ hasLocalData: function() {
+ return false;
+ },
+
+ loginMode: function () {
+ // PIN is set using this command:
+ // Clipperz.PM.PIN.setCredentialsWithPIN('1234', {'username':'joe', 'passphrase':'clipperz'});
+
+ return Clipperz.PM.PIN.isSet() ? 'PIN' : 'CREDENTIALS';
+ },
+
+ //=========================================================================
+
+ pages: function () {
+ return this._pages;
+ },
+
+ pageStack: function () {
+ return this._pageStack;
+ },
+
+ //=========================================================================
+
+ selectInitialProxy: function () {
+ if (this.isOnline()) {
+ this._proxy = Clipperz.PM.Proxy.defaultProxy;
+ } else {
+ if (this.hasLocalData()) {
+ this._proxy = new Clipperz.PM.Proxy.Offline({dataStore: new Clipperz.PM.Proxy.Offline.LocalStorageDataStore(), shouldPayTolls:false});
+ } else {
+ this.showOfflineError();
+ }
+ }
+ },
+
+ proxy: function () {
+ return this._proxy;
+ },
+
+ //=========================================================================
+
+ registerForNotificationCenterEvents: function () {
+ var events = ['doLogin', 'registerNewUser', 'showRegistrationForm', 'goBack', 'showRecord', 'searchCards', 'runDirectLogin'];
+ var self = this;
+
+ MochiKit.Base.map(function (anEvent) {
+ MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, anEvent, MochiKit.Base.method(self, anEvent));
+ }, events);
+
+// MochiKit.Signal.connect(window, 'onpopstate', MochiKit.Base.method(this, 'historyGoBack'));
+ MochiKit.Signal.connect(window, 'onbeforeunload', MochiKit.Base.method(this, 'shouldExitApp'));
+ },
+
+ //-------------------------------------------------------------------------
+
+ run: function (parameters) {
+ var shouldShowRegistrationForm;
+
+ this.selectInitialProxy();
+ shouldShowRegistrationForm = parameters['shouldShowRegistrationForm'] && this.proxy().canRegisterNewUsers();
+ this.pages()['loginPage'].setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable': this.proxy().canRegisterNewUsers()});
+
+ if (shouldShowRegistrationForm) {
+ this.showRegistrationForm();
+ } else {
+ this.showLoginForm();
+ }
+ this.overlay().done("", 0.5);
+ },
+
+ //-------------------------------------------------------------------------
+
+ showLoginForm: function () {
+ var loginFormPage;
+
+ loginFormPage = this.pages()['loginPage'];
+ loginFormPage.setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable': this.proxy().canRegisterNewUsers()});
+ this.moveInPage(this.currentPage(), 'loginPage');
+ MochiKit.Async.callLater(0.5, MochiKit.Base.method(loginFormPage, 'setInitialFocus'));
+ },
+
+ showRegistrationForm: function () {
+ var currentPage;
+ var registrationPage;
+
+ currentPage = this.currentPage();
+ registrationPage = this.pages()['registrationPage'];
+ this.setCurrentPage('loginPage');
+ registrationPage.setProps({});
+ this.moveInPage(currentPage, 'registrationPage');
+ MochiKit.Async.callLater(0.5, MochiKit.Base.method(registrationPage, 'setInitialFocus'));
+ },
+
+ //=========================================================================
+
+ doLogin: function (event) {
+ var credentials;
+ var getPassphraseDelegate;
+ var user;
+
+ user = null;
+
+ this.overlay().show("logging in");
+ this.pages()['loginPage'].setProps({disabled:true});
+
+ if ('pin' in event) {
+ credentials = Clipperz.PM.PIN.credentialsWithPIN(event['pin']);
+ } else {
+ credentials = event;
+ }
+ getPassphraseDelegate = MochiKit.Base.partial(MochiKit.Async.succeed, credentials.passphrase);
+ user = new Clipperz.PM.DataModel.User({'username':credentials.username, 'getPassphraseFunction':getPassphraseDelegate});
+
+ deferredResult = new Clipperz.Async.Deferred('MainController.doLogin', {trace:false});
+ deferredResult.addCallback(MochiKit.Async.wait, 0.1);
+ deferredResult.addMethod(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'deferredEntropyCollection');
+ deferredResult.addMethod(user, 'login');
+ deferredResult.addMethod(Clipperz.PM.PIN, 'resetFailedAttemptCount');
+ deferredResult.addMethod(this, 'setUser', user);
+
+// deferredResult.addMethod(this, 'setupApplication');
+ deferredResult.addMethod(this, 'runApplication');
+ deferredResult.addMethod(this.overlay(), 'done', "", 1);
+ deferredResult.addErrback(MochiKit.Base.method(this, 'genericErrorHandler', event));
+ deferredResult.addErrback(MochiKit.Base.bind(function (anEvent, anError) {
+ if (anError['isPermanent'] != true) {
+ this.pages()['loginPage'].setProps({disabled:false, 'mode':this.loginMode()});
+ this.pages()['loginPage'].setInitialFocus();
+ }
+ return anError;
+ }, this, event))
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ //-------------------------------------------------------------------------
+
+ registerNewUser: function (credentials) {
+ var deferredResult;
+
+ this.overlay().show("creating user");
+
+ this.pages()['registrationPage'].setProps({disabled:true});
+ deferredResult = new Clipperz.Async.Deferred('MainController.registerNewUser', {trace:false});
+ deferredResult.addCallback(Clipperz.PM.DataModel.User.registerNewAccount,
+ credentials['username'],
+ MochiKit.Base.partial(MochiKit.Async.succeed, credentials['passphrase'])
+ );
+ deferredResult.addMethod(this, 'doLogin', credentials);
+ deferredResult.addErrback(MochiKit.Base.method(this, 'genericErrorHandler', event));
+ deferredResult.addErrback(MochiKit.Base.bind(function (anError) {
+ if (anError['isPermanent'] != true) {
+ this.pages()['registrationPage'].setProps({disabled:false});
+ this.pages()['registrationPage'].setInitialFocus();
+ }
+ return anError;
+ }, this));
+
+ deferredResult.callback();
+
+ return deferredResult;
+
+ },
+
+ //-------------------------------------------------------------------------
+
+ user: function () {
+ return this._user;
+ },
+
+ setUser: function (aUser) {
+ this._user = aUser;
+ return this._user;
+ },
+
+ //=========================================================================
+
+ allCardInfo: function () {
+ var deferredResult;
+ var cardInfo;
+
+ cardInfo = {
+ '_rowObject': MochiKit.Async.succeed,
+ '_reference': MochiKit.Base.methodcaller('reference'),
+ '_searchableContent': MochiKit.Base.methodcaller('searchableContent'),
+ 'label': MochiKit.Base.methodcaller('label'),
+ 'favicon': MochiKit.Base.methodcaller('favicon')
+ };
+
+ deferredResult = new Clipperz.Async.Deferred('MainController.allCardInfo', {trace:false});
+ deferredResult.addMethod(this.user(), 'getRecords');
+ deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("CardList.value - collectResults", cardInfo, {trace:false}));
+ deferredResult.addCallback(Clipperz.Async.collectAll);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ filterCards: function (someCardInfo) {
+ var filter;
+ var filterRegExp;
+ var result;
+
+ filter = this.filter().replace(/[^A-Za-z0-9]/g, "\\$&");
+ filterRegExp = new RegExp(filter, "i");
+ result = MochiKit.Base.filter(function (aCardInfo) { return filterRegExp.test(aCardInfo['_searchableContent'])}, someCardInfo);
+
+ return result;
+ },
+
+ sortCards: function (someCardInfo) {
+ return someCardInfo.sort(Clipperz.Base.caseInsensitiveKeyComparator('label'));
+ },
+
+ showRecordList: function () {
+ var deferredResult;
+
+ deferredResult = new Clipperz.Async.Deferred('MainController.showRecordList', {trace:false});
+ deferredResult.addMethod(this, 'allCardInfo');
+ deferredResult.addMethod(this, 'filterCards');
+ deferredResult.addMethod(this, 'sortCards');
+ deferredResult.addCallback(MochiKit.Base.bind(function (someRecordInfo) {
+ this.pages()['cardListPage'].setProps({cardList: someRecordInfo});
+ }, this));
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ filter: function () {
+ return this._filter;
+ },
+
+ setFilter: function (aValue) {
+ this._filter = aValue;
+ },
+
+ searchCards: function (someParameters) {
+//console.log("SEARCH CARDS", someParameters);
+ this.setFilter(someParameters);
+ this.showRecordList();
+ },
+
+ //=========================================================================
+
+ runApplication: function () {
+ MochiKit.Signal.connect(window, 'onpopstate', MochiKit.Base.method(this, 'historyGoBack'));
+ this.moveInPage(this.currentPage(), 'cardListPage');
+ return this.showRecordList();
+ },
+
+ showRecord: function (aRecordReference) {
+//console.log("Show Record", aRecordReference);
+ var deferredResult;
+
+ this.pages()['cardListPage'].setProps({selectedCard:aRecordReference});
+ deferredResult = new Clipperz.Async.Deferred('MainController.runApplication', {trace:false});
+// deferredResult.addMethod(this.user(), 'getRecord', aRecordReference['_reference']);
+ deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
+ deferredResult.addMethodcaller('content');
+ deferredResult.addCallback(MochiKit.Base.bind(function (aCard) {
+//console.log("CARD DETAILS", aCard);
+ this.pages()['cardDetailPage'].setProps({card: aCard});
+ this.pages()['cardListPage'].setProps({selectedCard: null});
+ }, this));
+ deferredResult.addMethod(this, 'moveInPage', this.currentPage(), 'cardDetailPage', true);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ runDirectLogin: function (someParameters) {
+console.log("RUN DIRECT LOGIN", someParameters);
+ var deferredResult;
+
+// this.pages()['cardListPage'].setProps({selectedCard:aRecordReference});
+ deferredResult = new Clipperz.Async.Deferred('MainController.runDirectLogin', {trace:false});
+// deferredResult.addMethod(this.user(), 'getRecord', aRecordReference['_reference']);
+ deferredResult.addMethod(this.user(), 'getRecord', someParameters['record']);
+ deferredResult.addMethodcaller('directLoginWithReference', someParameters['directLogin']);
+ deferredResult.addCallback(Clipperz.PM.UI.DirectLoginRunner.openDirectLogin);
+ deferredResult.callback();
+
+ return deferredResult;
+ },
+
+ shouldExitApp: function (anEvent) {
+console.log("SHOULD EXIT APP");
+ anEvent.preventDefault();
+ anEvent.stopPropagation();
+ },
+
+ //=========================================================================
+
+ genericErrorHandler: function (anEvent, anError) {
+ var errorMessage;
+ var result;
+
+ result = anError;
+ errorMessage = "login failed";
+
+ if (anError['isPermanent'] === true) {
+ this.pages()['errorPage'].setProps({message:anError.message});
+ this.moveInPage(this.currentPage(), 'errorPage');
+ errorMessage = "failure";
+ } else {
+ if ('pin' in anEvent) {
+ errorCount = Clipperz.PM.PIN.recordFailedAttempt();
+ if (errorCount == -1) {
+ errorMessage = "PIN resetted";
+ }
+ }
+ }
+ this.overlay().failed(errorMessage, 1);
+
+ return result;
+ },
+
+ //=========================================================================
+
+ slidePage: function (fromPage, toPage, direction) {
+ var fromPosition;
+ var toPosition;
+
+ if (direction == "LEFT") {
+ fromPosition = 'right';
+ toPosition = 'left'
+ } else {
+ fromPosition = 'left';
+ toPosition = 'right'
+ }
+
+ MochiKit.DOM.addElementClass(fromPage, toPosition + ' transition');
+
+ MochiKit.DOM.addElementClass(toPage, fromPosition);
+ MochiKit.DOM.removeElementClass(toPage, toPosition);
+ MochiKit.DOM.addElementClass(toPage, 'transition');
+ MochiKit.Async.callLater(0.1, function () {
+ MochiKit.DOM.removeElementClass(toPage, fromPosition);
+ })
+
+ MochiKit.Async.callLater(0.5, function () {
+ MochiKit.DOM.removeElementClass(fromPage, 'transition');
+ MochiKit.DOM.removeElementClass(toPage, 'transition');
+ })
+ },
+
+ rotateInPage: function (fromPage, toPage) {
+ // Broken! :(
+ MochiKit.DOM.addElementClass(MochiKit.DOM.getElement('mainDiv'), 'show-right');
+ },
+
+ //.........................................................................
+
+ goBack: function () {
+ var fromPage;
+ var toPage;
+
+ fromPage = this.pageStack().shift();
+ toPage = this.currentPage();
+ this.pages()[toPage].setProps({});
+ this.moveOutPage(fromPage, toPage);
+ },
+
+ historyGoBack: function (anEvent) {
+ anEvent.preventDefault();
+ anEvent.stopPropagation();
+ this.goBack();
+ },
+
+ currentPage: function () {
+ return this.pageStack()[0];
+ },
+
+ setCurrentPage: function (aPage) {
+ this.pageStack().unshift(aPage);
+ },
+
+ moveInPage: function (fromPage, toPage, addToHistory) {
+ var shouldAddItemToHistory;
+
+ shouldAddItemToHistory = typeof(addToHistory) == 'undefined' ? false : addToHistory;
+
+ this.slidePage(MochiKit.DOM.getElement(fromPage), MochiKit.DOM.getElement(toPage), 'LEFT');
+ this.setCurrentPage(toPage);
+
+ if (shouldAddItemToHistory) {
+//console.log("ADD ITEM TO HISTORY");
+//console.log("ADD ITEM TO HISTORY - window", window);
+//console.log("ADD ITEM TO HISTORY - window.history", window.history);
+ window.history.pushState({'fromPage': fromPage, 'toPage': toPage});
+//# window.history.pushState();
+//console.log("ADDED ITEM TO HISTORY");
+ } else {
+//console.log("Skip HISTORY");
+ }
+ },
+
+ moveOutPage: function (fromPage, toPage) {
+ this.slidePage(MochiKit.DOM.getElement(fromPage), MochiKit.DOM.getElement(toPage), 'RIGHT');
+ this.setCurrentPage(toPage);
+ },
+
+ //=========================================================================
+/*
+ wrongAppVersion: function (anError) {
+// this.pages()['errorPage'].setProps({message:anError.message});
+// this.moveInPage('errorPage', this.currentPage());
+ },
+*/
+ //=========================================================================
+ __syntaxFix__: "syntax fix"
+});