Ext.namespace('Zarafa.plugins.passwd');

/**
 * @class Zarafa.plugins.passwd.ABOUT
 *
 * The copyright string holding the copyright notice for the Zarafa passwd Plugin.
 */
Zarafa.plugins.passwd.ABOUT = ""
	+ "<p>Copyright (C) 2013  Saket Patel &lt;silentsakky@gmail.com&gt;</p>"

	+ "<p>This program 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.</p>"

	+ "<p>This program 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.</p>"

	+ "<p>You should have received a copy of the GNU Affero General Public License "
	+ "along with this program. If not, see <a href=\"http://www.gnu.org/licenses/\" target=\"_blank\">http://www.gnu.org/licenses/</a>.</p>";Ext.namespace('Zarafa.plugins.passwd');

/**
 * @class Zarafa.plugins.passwd.PasswdPlugin
 * @extends Zarafa.core.Plugin
 *
 * Passwd plugin.
 * Allows users to change password from webapp.
 */
Zarafa.plugins.passwd.PasswdPlugin = Ext.extend(Zarafa.core.Plugin, {

	/**
	 * Initialize the plugin by registering to the insertion point
	 * to add something to the right end of the main tab bar.
	 * @protected
	 */
	initPlugin : function()
	{
		Zarafa.plugins.passwd.PasswdPlugin.superclass.initPlugin.apply(this, arguments);

		// Register categories for the settings
		this.registerInsertionPoint('context.settings.categories', this.createSettingsCategory, this);
	},

	/**
	 * Create the delegate {@link Zarafa.settings.ui.SettingsCategory Settings Category}
	 * to the {@link Zarafa.settings.SettingsContext}. This will create new
	 * {@link Zarafa.settings.ui.SettingsCategoryTab tabs} for the
	 * {@link Zarafa.calendar.ui.SettingsPasswdCategory Password}
	 * in the {@link Zarafa.settings.ui.SettingsCategoryWidgetPanel Widget Panel}.
	 * @param {String} insertionName insertion point name that is currently populated
	 * @param {Zarafa.settings.ui.SettingsMainPanel} settingsMainPanel settings main panel
	 * which is populating this insertion point
	 * @param {Zarafa.settings.SettingsContext} settingsContext settings context
	 * @return {Array} configuration object for the categories to register
	 * @private
	 */
	createSettingsCategory : function(insertionName, settingsMainPanel, settingsContext)
	{
		return {
			xtype : 'zarafa.settingspasswdcategory',
			settingsContext : settingsContext
		};
	}
});

Zarafa.onReady(function() {
	container.registerPlugin(new Zarafa.core.PluginMetaData({
		name : 'passwd',
		displayName : dgettext("plugin_passwd", 'Password Change Plugin'),
		about : Zarafa.plugins.passwd.ABOUT,
		pluginConstructor : Zarafa.plugins.passwd.PasswdPlugin
	}));
});
Ext.namespace('Zarafa.plugins.passwd.data');

/**
 * @class Zarafa.plugins.passwd.data.ResponseHandler
 * @extends Zarafa.core.data.AbstractResponseHandler
 *
 * Passwd plugin specific response handler.
 */
Zarafa.plugins.passwd.data.PasswdResponseHandler = Ext.extend(Zarafa.core.data.AbstractResponseHandler, {

	/**
	 * @cfg {Function} callbackFn The function which will be called after success/failure response.
	 */
	callbackFn : undefined,

	/**
	 * @cfg {Object} scope The function scope that will be used when calling {@link #callbackFn}.
	 */
	scope : undefined,

	/**
	 * In case exception happened on server, server will return exception response with the display message.
	 * @param {Object} response Object contained the response data.
	 */
	doError : function(response)
	{
		var displayMessage = dgettext("plugin_passwd", 'An unknown error occurred while changing password.');

		if(response.info) {
			displayMessage = response.info.display_message;
		}

		Ext.MessageBox.alert(dgettext("plugin_passwd", 'Error'), displayMessage);

		this.callbackFn.apply(this.scope || this, [ false, response ]);
	},

	/**
	 * When password change is successfull server will send a success response including display message.
	 * @param {Object} response Object contained the response data.
	 */
	doSuccess : function(response)
	{
		var displayMessage = dgettext("plugin_passwd", 'Password is changed successfully.');

		if(response.info) {
			displayMessage = response.info.display_message;
		}

		Ext.MessageBox.alert(dgettext("plugin_passwd", 'Success'), displayMessage);

		this.callbackFn.apply(this.scope || this, [ true, response ]);
	}
});
Ext.namespace('Ext.ux.form.field');

/**
 * @class Ext.ux.form.field.PasswordMeter
 * @extends Ext.form.TextField
 * @xtype ux.passwordmeterfield
 *
 * @author Christoph Haas <christoph.h@sprinternet.at>
 * @version 0.1
 * @license MIT License: http://www.opensource.org/licenses/mit-license.php
 *
 * This implementation of the password fields shows a nice graph of how
 * secure the password is.
 * Original implementation for ExtJS 1.1 from http://testcases.pagebakers.com/PasswordMeter/.
 */
Ext.ux.form.field.PasswordMeter = Ext.extend(Ext.form.TextField, {

	/**
	 * @constructor
	 * @param {Object} config configuration object
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'ux.passwordmeterfield',
			inputType: 'password',
			enableKeyEvents: true
		});

		Ext.ux.form.field.PasswordMeter.superclass.constructor.call(this, config);
	},

	// private
	initComponent:function()
	{
		Ext.ux.form.field.PasswordMeter.superclass.initComponent.apply(this, arguments);
	},

	// private
	reset: function()
	{
		Ext.ux.form.field.PasswordMeter.superclass.reset.call(this);
		this.updateMeter();
	},

	// private
	onKeyUp : function(event)
	{
		Ext.ux.form.field.PasswordMeter.superclass.onKeyUp.call(this);

		this.updateMeter(this.getValue());

		this.fireEvent('keyup', this, event);
	},

	// private
	afterRender: function()
	{
		Ext.ux.form.field.PasswordMeter.superclass.afterRender.call(this);


		var width = this.getEl().getWidth();
		this.strengthMeterID = newID = Ext.id();
		this.scoreBarID = Ext.id();
		var objMeter = Ext.DomHelper.insertAfter(this.getEl(), {
			tag: "div",
			'class': "x-form-strengthmeter",
			'id': this.strengthMeterID,
			'style' : {
				width: width + 'px'
			}
		});
		Ext.DomHelper.append(objMeter, {
			tag: "div",
			'class': "x-form-strengthmeter-scorebar",
			'id': this.scoreBarID
		});

		this.fireEvent('afterrender', this);
	},

	/**
	 * Return the score of the entered password.
	 * It is a number between 0 and 100 where 100 is a very safe password.
	 *
	 * @returns {Number}
	 */
	getScore : function()
	{
		return this.calcStrength(this.getValue());
	},

	/**
	 * Sets the width of the meter, based on the score
	 *
	 * @param {String} val The current password
	 */
	updateMeter : function(val)
	{
		var maxWidth, score, scoreWidth, objMeter, scoreBar;

		objMeter = Ext.get(this.strengthMeterID);
		scoreBar = Ext.get(this.scoreBarID);

		maxWidth = objMeter.getWidth();
		if (val){
			score = this.calcStrength(val);
			scoreWidth = maxWidth - (maxWidth / 100) * score;
			scoreBar.applyStyles({margin: "0 0 0 " + (maxWidth - scoreWidth) + "px"}); // move the overlay to the right
			scoreBar.setWidth(scoreWidth, false); // downsize the overlay
		} else {
			scoreBar.applyStyles({margin: "0"});
			scoreBar.setWidth(maxWidth, false);
		}
	},

	/**
	 * Calculates the strength of a password
	 *
	 * @param {Object} p The password that needs to be calculated
	 * @return {int} intScore The strength score of the password
	 */
	calcStrength: function(p)
	{
		// PASSWORD LENGTH
		var len = p.length, score = len;

		if (len > 0 && len <= 4) { // length 4 or
			// less
			score += len
		} else if (len >= 5 && len <= 7) {
			// length between 5 and 7
			score += 6;
		} else if (len >= 8 && len <= 15) {
			// length between 8 and 15
			score += 12;
		} else if (len >= 16) { // length 16 or more
			score += 18;
		}

		// LETTERS (Not exactly implemented as dictacted above
		// because of my limited understanding of Regex)
		if (p.match(/[a-z]/)) {
			// [verified] at least one lower case letter
			score += 1;
		}
		if (p.match(/[A-Z]/)) { // [verified] at least one upper
			// case letter
			score += 5;
		}
		// NUMBERS
		if (p.match(/\d/)) { // [verified] at least one
			// number
			score += 5;
		}
		if (p.match(/(?:.*?\d){3}/)) {
			// [verified] at least three numbers
			score += 5;
		}

		// SPECIAL CHAR
		if (p.match(/[\!,@,#,$,%,\^,&,\*,\?,_,~]/)) {
			// [verified] at least one special character
			score += 5;
		}
		// [verified] at least two special characters
		if (p.match(/(?:.*?[\!,@,#,$,%,\^,&,\*,\?,_,~]){2}/)) {
			score += 5;
		}

		// COMBOS
		if (p.match(/(?=.*[a-z])(?=.*[A-Z])/)) {
			// [verified] both upper and lower case
			score += 2;
		}
		if (p.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/)) {
			// [verified] both letters and numbers
			score += 2;
		}
		// [verified] letters, numbers, and special characters
		if (p.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\!,@,#,$,%,\^,&,\*,\?,_,~])/)) {
			score += 2;
		}

		return Math.min(Math.round(score * 2), 100);
	}
});

Ext.reg('ux.passwordmeterfield', Ext.ux.form.field.PasswordMeter);Ext.namespace('Zarafa.plugins.passwd.settings');

/**
 * @class Zarafa.plugins.passwd.settings.PasswdPanel
 * @extends Ext.form.FormPanel
 *
 * Panel which holds a form that will be used to change the password
 */
Zarafa.plugins.passwd.settings.PasswdPanel = Ext.extend(Ext.form.FormPanel, {

	/**
	 * @constructor
	 * @param {Object} config configuration object that needs to be used when creating this dialog
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'zarafa.passwdpanel',
			labelWidth : 200,
			defaults : {
				width : 200
			},
			border : false,
			items : [{
				xtype : 'displayfield',
				name : 'username',
				fieldLabel : dgettext("plugin_passwd", 'User name')
			}, {
				xtype : 'textfield',
				name : 'current_password',
				ref : 'current_password',
				fieldLabel : dgettext("plugin_passwd", 'Current password'),
				inputType : 'password',
				allowBlank : false,
				listeners : {
					change : this.onFieldChange,
					scope : this
				}
			}, {
				xtype : 'ux.passwordmeterfield',
				name : 'new_password',
				ref : 'new_password',
				allowBlank : false,
				fieldLabel : dgettext("plugin_passwd", 'New password'),
				listeners : {
					change : this.onFieldChange,
					scope : this
				}
			}, {
				xtype : 'ux.passwordmeterfield',
				name : 'new_password_repeat',
				allowBlank : false,
				ref : 'new_password_repeat',
				fieldLabel : dgettext("plugin_passwd", 'Retype new password'),
				listeners : {
					change : this.onFieldChange,
					scope : this
				}
			}]
		});

		this.addEvents(
			/**
			 * @event userchange
			 * Fires when a field is modified in the form panel
			 */
			'userchange'
		);

		Zarafa.plugins.passwd.settings.PasswdPanel.superclass.constructor.apply(this, arguments);

		this.on('afterrender', this.initialize, this);
	},

	/**
	 * Function will initialize this dialog with some default values and will
	 * also create object of {@link #saveMask}.
	 */
	initialize : function()
	{
		this.getForm().setValues({
			username : container.getUser().getUserName()
		});
	},

	/**
	 * Handler function will be called when user changes any field in the form.
	 * This will fire custom event on this form to indicate that settings model
	 * should be marked as dirty
	 */
	onFieldChange : function(field, newValue, oldValue)
	{
		this.fireEvent('userchange', this);
	}
});

Ext.reg('zarafa.passwdpanel', Zarafa.plugins.passwd.settings.PasswdPanel);
Ext.namespace('Zarafa.plugins.passwd.settings');

/**
 * @class Zarafa.plugins.passwd.settings.SettingsPasswdCategory
 * @extends Zarafa.settings.ui.SettingsCategory
 * @xtype zarafa.settingspasswdcategory
 *
 * The passwd settings category that will allow users to change their passwords
 */
Zarafa.plugins.passwd.settings.SettingsPasswdCategory = Ext.extend(Zarafa.settings.ui.SettingsCategory, {
	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			title : dgettext("plugin_passwd", 'Change Password'),
			categoryIndex : 9997,
			iconCls : 'zarafa-settings-category-passwd',
			xtype : 'zarafa.settingspasswdcategory',
			items : [{
				xtype : 'zarafa.settingspasswdwidget',
				settingsContext : config.settingsContext
			},
				container.populateInsertionPoint('context.settings.category.passwd', this)
			]
		});

		Zarafa.plugins.passwd.settings.SettingsPasswdCategory.superclass.constructor.call(this, config);
	}
});

Ext.reg('zarafa.settingspasswdcategory', Zarafa.plugins.passwd.settings.SettingsPasswdCategory);
Ext.namespace('Zarafa.plugins.passwd.settings');

/**
 * @class Zarafa.plugins.passwd.settings.SettingsPasswdWidget
 * @extends Zarafa.settings.ui.SettingsWidget
 * @xtype zarafa.settingspasswdwidget
 *
 * The {@link Zarafa.settings.ui.SettingsWidget widget} for changing password
 * in the {@link Zarafa.plugins.passwd.settings.SettingsPasswdCategory password category}.
 */
Zarafa.plugins.passwd.settings.SettingsPasswdWidget = Ext.extend(Zarafa.settings.ui.SettingsWidget, {

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			title : dgettext("plugin_passwd", 'Change Password'),
			xtype : 'zarafa.settingspasswdwidget',
			layout: 'form',
			items : [{
				xtype : 'zarafa.passwdpanel',
				ref : 'passwdPanel',
				listeners : {
					userchange : this.setModelDirty,
					scope : this
				}
			}]
		});

		Zarafa.plugins.passwd.settings.SettingsPasswdWidget.superclass.constructor.call(this, config);
	},

	/**
	 * initialize events for the {@link Zarafa.plugins.passwd.settings.SettingsPasswdWidget SettingsPasswdWidget}.
	 * @private
	 */
	initEvents : function()
	{
		Zarafa.plugins.passwd.settings.SettingsPasswdWidget.superclass.initEvents.call(this);

		// listen to savesettings and discardsettings to save/discard delegation data
		var contextModel = this.settingsContext.getModel();

		this.mon(contextModel, 'beforesavesettings', this.onBeforeSaveSettings, this);
		this.mon(contextModel, 'savesettings', this.onSaveSettings, this);
		this.mon(contextModel, 'discardsettings', this.onDiscardSettings, this);
	},

	/**
	 * Event handler will be called when {@link Zarafa.settings.SettingsContextModel#beforesavesettings} event is fired.
	 * This function will validate the formdata.
	 *
	 * @private
	 */
	onBeforeSaveSettings : function()
	{
		// do some quick checks before submitting
		if(this.passwdPanel.new_password.getValue() != this.passwdPanel.new_password_repeat.getValue()) {
			Ext.MessageBox.alert(dgettext("plugin_passwd", 'Error'), dgettext("plugin_passwd", 'New passwords do not match.'));
			return false;
		} else if(Ext.isEmpty(this.passwdPanel.current_password.getValue())) {
			Ext.MessageBox.alert(dgettext("plugin_passwd", 'Error'), dgettext("plugin_passwd", 'Current password is empty.'));
			return false;
		} else if(Ext.isEmpty(this.passwdPanel.new_password.getValue()) || Ext.isEmpty(this.passwdPanel.new_password_repeat.getValue())) {
			Ext.MessageBox.alert(dgettext("plugin_passwd", 'Error'), dgettext("plugin_passwd", 'New password is empty.'));
			return false;
		} else if(!this.passwdPanel.getForm().isValid()) {
			Ext.MessageBox.alert(dgettext("plugin_passwd", 'Error'), dgettext("plugin_passwd", 'One or more fields does contain errors.'));
			return false;
		} else {
			// do a quick score check:
			if(this.passwdPanel.new_password.getScore() < 70) {
				Ext.MessageBox.alert(dgettext("plugin_passwd", 'Error'), dgettext("plugin_passwd", 'Password is weak. Password should contain capital, non-capital letters and numbers. Password should have 8 to 20 characters.'));
				return false;
			}
			return true;
		}
	},

	/**
	 * Event handler will be called when {@link Zarafa.settings.SettingsContextModel#savesettings} event is fired.
	 * This will relay this event to {@link Zarafa.plugins.passwd.settings.PasswdPanel PasswdPanel} so it can
	 * save data.
	 * @private
	 */
	onSaveSettings : function()
	{
		// only save when this category is visible on screen
		if(this.ownerCt.isVisible()) {
			this.ownerCt.displaySavingMask();

			var data = this.passwdPanel.getForm().getFieldValues();

			// send request
			container.getRequest().singleRequest('passwdmodule', 'save', data, new Zarafa.plugins.passwd.data.PasswdResponseHandler({
				callbackFn: function (success, response) {
					this.ownerCt.hideSavingMask(success);
					if(success) {
						this.passwdPanel.getForm().reset();
					}
				},
				scope : this
			}));
		}
	},

	/**
	 * Event handler will be called when {@link Zarafa.settings.SettingsContextModel#discardsettings} event is fired.
	 * This will relay this event to {@link Zarafa.plugins.passwd.settings.PasswdPanel PasswdPanel} so it can
	 * discard current changes.
	 * @private
	 */
	onDiscardSettings : function()
	{
		this.passwdPanel.getForm().reset();
	},

	/**
	 * Function will be called when any field in {@link Zarafa.plugins.passwd.settings.PasswdPanel}
	 * is changed and we need to mark settings model as dirty.
	 * @private
	 */
	setModelDirty : function()
	{
		var model = this.settingsContext.getModel();

		if(!model.hasChanges()) {
			model.setDirty();
		}
	}
});

Ext.reg('zarafa.settingspasswdwidget', Zarafa.plugins.passwd.settings.SettingsPasswdWidget);
