dojo.provide("dojox.storage.encrypted");
dojo.require("dojox.storage");

dojo.require("dojox.encoding.crypto.Blowfish");

(function(){

// we need to broaden the range of allowed
// characters for keys so our Blowfish keys
// are accepted.
dojox.storage.isValidKey = function(/*string*/ keyName){ /*Boolean*/
	if(keyName === null || keyName === undefined){
		return false;
	}
	return /^[\/=0-9A-Za-z_+-]*$/.test(keyName);
};

var es = dojo.mixin({},dojox.storage,{
	// summary:
	//		This creates dojox.storage.encrypted.
	//		It exposes the same methods as dojox.storage,
	//		but encrypts all keys and values. In
	//		addition to dojox.storage, it provides the
	//		getPassphrase() / setPassphrase() methods.
	//		It uses dojox.encoding.crypto.Blowfish, but
	//		could use any given crypto engine.
	//	example:
	//		dojo.require("dojox.storage.encrypted");
	//		var mySto = dojox.storage.encrypted;
	//		mySto.setPassphrase("my secret passphrase");
	//		mySto.put('key','value'); // key and value get encrypted
	//		var value = mySto.get('key'); // value contains the unencrypted value
	
	key: '',
	
	crypto: dojox.encoding.crypto.Blowfish,
	
	setPassphrase: function(/* String */ key){
		//	summary:
		//		Sets the passphrase used for
		//		de- / encryption of data.
		//	key: String
		//		The passphrase to use.
		this.key = key;
	},
	
	getPassphrase: function(){
		//	summary:
		//		Returns the current passphrase.
		return this.key;
	},
	
	get: function(/*string*/ key, /*string?*/ namespace){ /*Object*/
		return dojo.fromJson(
			this.decrypt(
				dojox.storage.get(this.encrypt(key), namespace)
			)
		);
	},

	getMultiple: function(/*array*/ keys, /*string?*/ namespace){ /*Object*/
		var results = []; 
		for(var i = 0; i < keys.length; i++){ 
			results.push(this.get(keys[i], namespace)); 
		} 
		
		return results;
	},
	
	put: function(	/*string*/ key,
					/*object*/ value, 
					/*function*/ resultsHandler,
					/*string?*/ namespace){
		return dojox.storage.put(
			this.encrypt(key),
			this.encrypt(dojo.toJson(value)),
			resultsHandler,
			namespace
		);
	},
		
	putMultiple: function(	/*array*/ keys,
							/*array*/ values, 
							/*function*/ resultsHandler,
							/*string?*/ namespace){
		
		for(var i = 0; i < keys.length; i++){ 
			this.put(keys[i], values[i], resultsHandler, namespace); 
		}
	},
	
	remove: function(/*string*/ key, /*string?*/ namespace){
		dojox.storage.remove(this.encrypt(key),namespace);
	},

	removeMultiple: function(/*array*/ keys, /*string?*/ namespace) {
		for(var i = 0; i < keys.length; i++){ 
			this.remove(keys[i], namespace); 
		}
	},
	
	clear: function(/*string?*/ namespace){
		dojo.forEach(this.getKeys(namespace),function(key){
			this.remove(key);
		},this);
	},
	
	/* --- utilities --- */

	hasKey: function(/*string*/ key, /*string?*/ namespace){
		return !!this.get(key, namespace); // Boolean
	},
	
	getKeys: function(/*string?*/ namespace){ /*Array*/
		var cipherKeys = dojox.storage.getKeys(namespace),
			plainKeys = [];
		dojo.forEach(cipherKeys,function(key){
			plainKeys.push(this.decrypt(key));
		},this);
		return plainKeys;
	},
	
	encrypt: function(/* string */ plaintext){
		//	summary:
		//		Calls the given crypto engine
		//		to encrypt a string.
		return plaintext && plaintext.split ? this.crypto.encrypt(plaintext,this.key) : null; // Added check for null value, as reported by YC
	},
	
	decrypt: function(/* string */ ciphertext){
		//	summary:
		//		Calls the given crypto engine
		//		to decrypt a string.
		return ciphertext && ciphertext.split ? this.crypto.decrypt(ciphertext,this.key) : null; // Added check for null value, as reported by YC
	}
	
});

dojox.storage.encrypted = es;

})();
