From 20bea94ab6b91c85b171dcf86baba0a64169d508 Mon Sep 17 00:00:00 2001 From: Giulio Cesare Solaroli Date: Fri, 30 Aug 2013 15:56:53 +0000 Subject: First release of /delta version --- (limited to 'frontend/delta/js/Clipperz/PM/DataModel') 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 = + 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", {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 ", { + '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", { + '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 ", { + '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(""); + } 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 ", [ + 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 ", {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 ", {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", {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", {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 ", {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") +}; + +//----------------------------------------------------------------------------- -- cgit v0.9.0.2