Ext.namespace('Zarafa.plugins.smime.data');

/**
 * @class Zarafa.plugins.smime.data.JsonCertificateReader
 * @extends Zarafa.core.data.JsonReader
 */
Zarafa.plugins.smime.data.JsonCertificateReader = Ext.extend(Zarafa.core.data.JsonReader, {
        /**
         * @cfg {Zarafa.core.data.RecordCustomObjectType} customObjectType The custom object type
         * which represents the {@link Ext.data.Record records} which should be created using
         * {@link Zarafa.core.data.RecordFactory#createRecordObjectByCustomType}.
         */
        customObjectType : Zarafa.core.data.RecordCustomObjectType.ZARAFA_SMIME,

        /**
         * @constructor
         * @param {Object} meta Metadata configuration options.
         * @param {Object} recordType (optional) Optional Record type matches the type
         * which must be read from response. If no type is given, it will use the
         * record type for the {@link Zarafa.core.data.RecordCustomObjectType#ZARAFA_SMIME}.
         */
        constructor : function(meta, recordType)
        {
                meta = Ext.applyIf(meta || {}, {
                        dynamicRecord : false
                });

		recordType = Zarafa.core.data.RecordFactory.getRecordClassByCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_SMIME);

                Zarafa.plugins.smime.data.JsonCertificateReader.superclass.constructor.call(this, meta, recordType);
        }
});
Ext.namespace('Zarafa.plugins.smime.data');

/**
 * @class Zarafa.plugins.smime.data.SmimeAttachmentRecordFields
 * Array of {@link Ext.data.Field field} configurations for the
 * {@link Zarafa.plugins.smime.data.SmimeAttachmentRecord} object.
 */
Zarafa.plugins.smime.data.SmimeAttachmentRecordFields = [
	{name: 'cert', type: 'boolean', defaultValue: false}, 
	{name: 'cert_message', type: 'string'},
	{name: 'cert_warning', type: 'string'}
];

Zarafa.core.data.RecordCustomObjectType.addProperty('ZARAFA_SMIME_ATTACHMENT');
Zarafa.core.data.RecordFactory.setBaseClassToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_SMIME_ATTACHMENT, Zarafa.plugins.smime.data.SmimeAttachmentRecord);
Zarafa.core.data.RecordFactory.addFieldToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_SMIME_ATTACHMENT, Zarafa.plugins.smime.data.SmimeAttachmentRecordFields);
Zarafa.core.data.RecordFactory.addFieldToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_SMIME_ATTACHMENT, Zarafa.core.data.IPMAttachmentRecordFields);
Ext.namespace('Zarafa.plugins.smime.data');

/**
 * @class Zarafa.plugins.smime.data.SmimeAttachmentStore
 * @extends Zarafa.core.data.IPMAttachmentStore
 * AttachmentStore specific for S/MIME Plugin which creates {@link Zarafa.plugins.smime.SmimeAttachmentRecord record}.
 * @private
 */
Zarafa.plugins.smime.data.SmimeAttachmentStore = Ext.extend(Zarafa.core.data.IPMAttachmentStore, {
	/**
	 * @cfg {Zarafa.core.data.RecordCustomObjectType} smime objecttype for the creation of our own attachmenttype in the attachmentstore
	 */
	attachmentRecordType : Zarafa.core.data.RecordCustomObjectType.ZARAFA_SMIME_ATTACHMENT,

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

		Zarafa.plugins.smime.data.SmimeAttachmentStore.superclass.constructor.call(this, config);
	}
});

Ext.reg('smime.attachmentstore', Zarafa.plugins.smime.data.SmimeAttachmentStore);
Ext.namespace('Zarafa.plugins.smime.data');

/**
 * @class Zarafa.plugins.smime.data.SmimeCertificateRecordFields
 * Array of {@link Ext.data.Field field} configurations for the
 * {@link Zarafa.plugins.smime.data.SmimeCertificateRecord} object.
 */
Zarafa.plugins.smime.data.SmimeCertificateRecordFields = [
	{name: 'entryid', type: 'string'},
	{name: 'type', type: 'string'}, // Public or Private
	{name: 'issued_by', type: 'string'},
	{name: 'issued_to', type: 'string'},
	{name: 'serial', type: 'string'},
	{name: 'email', type: 'string', defaultValue: ''},
	{name: 'validto', type: 'date', dateFormat: 'timestamp', defaultValue: null},
	{name: 'validfrom', type: 'date', dateFormat: 'timestamp', defaultValue: null},
	{name: 'fingerprint_sha1', type: 'string'},
	{name: 'fingerprint_md5', type: 'string'},
	{name: 'key_type', type: 'string', defaultValue: 'unknown'},
	{name: 'key_bits', type: 'int', defaultValue: 0},
	{name: 'curve_name', type: 'string', defaultValue: ''},
	{name: 'purpose', type: 'string', defaultValue: 'both'}
];

/**
 * @class Zarafa.plugins.smime.data.SmimeCertificateRecord
 * @extends Zarafa.core.data.IPMRecord
 *
 * An extension to the {@link Zarafa.core.data.IPMRecord}.
 */
Zarafa.plugins.smime.data.SmimeCertificateRecord = Ext.extend(Zarafa.core.data.IPMRecord, {});

Zarafa.core.data.RecordCustomObjectType.addProperty('ZARAFA_SMIME');
Zarafa.core.data.RecordFactory.addFieldToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_SMIME, Zarafa.plugins.smime.data.SmimeCertificateRecordFields);
Zarafa.core.data.RecordFactory.addFieldToCustomType(Zarafa.core.data.RecordCustomObjectType.MAPI_SMIME_ATTACH, Zarafa.core.data.IPMRecordFields);
Zarafa.core.data.RecordFactory.setSubStoreToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_SMIME, 'attachments', Zarafa.plugins.smime.data.SmimeAttachmentStore);
Zarafa.core.data.RecordFactory.addListenerToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_SMIME, 'createphantom', function(record)
{
	// Phantom records must always be marked as opened (they contain the full set of data)
	record.afterOpen();
});

Zarafa.core.data.RecordFactory.setBaseClassToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_SMIME, Zarafa.plugins.smime.data.SmimeCertificateRecord);
Ext.namespace('Zarafa.plugins.smime.data');

/**
 * @class Zarafa.plugins.smime.data.SmimeCertificateStore
 * @extends Zarafa.core.data.ListModuleStore
 * @xtype smime.certificatestore
 * Store specific for S/MIME Plugin which creates {@link Zarafa.plugins.smime.SmimeCertificateRecord record}.
 */
Zarafa.plugins.smime.data.SmimeCertificateStore = Ext.extend(Zarafa.core.data.ListModuleStore, {
	/**
	 * @constructor
	 * @param config Configuration object
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			autoLoad : true,
			remoteSort: false,
			reader : new Zarafa.plugins.smime.data.JsonCertificateReader(),
			writer : new Zarafa.core.data.JsonWriter(),
			proxy  : new Zarafa.core.data.IPMProxy({
				listModuleName: 'pluginsmimemodule',
				itemModuleName: 'pluginsmimemodule'
			})
		});

		Zarafa.plugins.smime.data.SmimeCertificateStore.superclass.constructor.call(this, config);
	}
});

Ext.reg('smime.certificatestore', Zarafa.plugins.smime.data.SmimeCertificateStore);
Ext.namespace('Zarafa.plugins.smime.data');

/**
 * @class Zarafa.plugins.smime.data.SmimeResponseHandler
 * @extends Zarafa.core.data.AbstractResponseHandler
 *
 * Smime specific response handler.
 */
Zarafa.plugins.smime.data.SmimeResponseHandler = Ext.extend(Zarafa.core.data.AbstractResponseHandler, {

	/**
	 * @cfg {Function} successCallback The function which
	 * will be called after success request.
	 */
	successCallback : null,
	
	/**
	 * @param {Object} response Object contained the response data.
	 */
	doCertificate : function(response) {
		this.successCallback(response);
	},

	/**
	 * @param {Object} response Object contained the response data.
	 */
	doPassphrase : function(response) {
		this.successCallback(response);
	},

	/**
	 * @param {Object} response Object contained the response data.
	 */
	doChangepassphrase : function(response) {
		this.successCallback(response);
	}
});

Ext.reg('smime.responsehandler', Zarafa.plugins.smime.data.SmimeResponseHandler);
Ext.namespace('Zarafa.plugins.smime.dialogs');

/**
 * @class Zarafa.plugins.smime.dialogs.ChangePassphraseContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 *
 * The content panel for changing a users passphrase.
 * @xtype smime.changepassphrasepanel
 */
Zarafa.plugins.smime.dialogs.ChangePassphraseContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {
	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};
		config = Ext.applyIf(config, {
			layout: 'fit',
			title: _('Change passphrase'),
			width: 300,
			height: 250,
			stateful: false,
			xtype: 'smime.changepassphrasecontentpanel',
			items: [{
				xtype: 'smime.changepassphrasepanel',
				buttonAlign: 'center',
				buttons: [{
					text: _('Change passphrase'),
					handler: this.onChangePassphrase,
					scope: this
				},{
					text: _('Cancel'),
					handler: this.close,
					scope: this
				}]
			}]
		});

		Zarafa.plugins.smime.dialogs.ChangePassphraseContentPanel.superclass.constructor.call(this, config);
	},

	/**
	 * Change passphrase function, if the two new passphrases are equal they
	 * new passphrase is sent to the server.
	 */
	onChangePassphrase: function()
	{
		var form = this.get(0);

		if (form.validatePassphrase()) {
			form.changePassphrasePanel();
		}
	}
});

Ext.reg('smime.changepassphrasecontentpanel', Zarafa.plugins.smime.dialogs.ChangePassphraseContentPanel);
Ext.namespace('Zarafa.plugins.smime.dialogs');

/**
 * @class Zarafa.plugins.smime.dialogs.ChangePassphrasePanel
 * @extends Ext.Panel
 *
 * The panel containing the form for changing a users passphrase.
 * @xtype smime.changepassphrasepanel
 */
Zarafa.plugins.smime.dialogs.ChangePassphrasePanel = Ext.extend(Ext.Panel, {
	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			xtype: 'smime.changepassphrasepanel',
			layout: 'form',
			border: false,
			header: false,
			items: [{
				hideLabel: true,
				xtype: 'displayfield',
				value: _('Old passphrase')
			},{
				fieldLabel: _('Old passphrase'),
				hideLabel: true,
				xtype : 'textfield',
				inputType: 'password',
				anchor: '100%',
				ref : 'old_passphrase',
				listeners : {
					'change': this.resetVerification,
					scope: this
				}
			},{
				hideLabel: true,
				hidden: true,
				hideMode: 'visibility',
				xtype: 'displayfield',
				ref: 'wrong_passphrase',
				cls: 'zarafa-smime-invalid-text',
				value: _('Wrong passphrase. Please try again')
			},{
				hideLabel: true,
				xtype: 'displayfield',
				value: _('New passphrase')
			},{
				fieldLabel: _('New passphrase'),
				hideLabel: true,
				xtype : 'textfield',
				inputType: 'password',
				anchor: '100%',
				listeners : {
					'change': this.resetVerification,
					scope: this
				},
				ref : 'new_passphrase'
			},{
				hideLabel: true,
				xtype: 'displayfield',
				value: _('Confirm new passphrase')
			},{
				fieldLabel: _('Confirm new passphrase'),
				hideLabel: true,
				xtype : 'textfield',
				inputType: 'password',
				anchor: '100%',
				listeners : {
					'change': this.resetVerification,
					scope: this
				},
				ref : 'confirm_new_passphrase'
			},{
				hideLabel: true,
				hidden: true,
				xtype: 'displayfield',
				ref: 'wrong_verification',
				cls: 'zarafa-smime-invalid-text',
				value: _("The passphrases don't match. Please try again")
			}]
		});

		Zarafa.plugins.smime.dialogs.ChangePassphrasePanel.superclass.constructor.call(this, config);
	},

	/**
	 * Verifies the new passphrase and hides or shows the error message.
	 * @return {Boolean} returns false when new passphrase and confirmation passphrase
	 * don't match otherwise it returns true.
	 */
	validatePassphrase: function()
	{
		if (this.new_passphrase.getValue() !== this.confirm_new_passphrase.getValue()) {
			this.wrong_verification.show();
			return false;
		} else if (!this.wrong_verification.hidden) {
			this.wrong_verification.hide();
		}

		return true;
	},

	/**
	 * Reset verification fields when a user changes one of the password fields.
	 */
	resetVerification: function()
	{
		this.wrong_verification.hide();
		this.wrong_passphrase.hide();
	},

	/**
	 * Sends passphrase change request to the backend
	 */
	changePassphrasePanel: function()
	{
		container.getRequest().singleRequest(
			'pluginsmimemodule',
			'changepassphrase',
			{
				'passphrase': this.old_passphrase.getValue(),
				'new_passphrase': this.new_passphrase.getValue()
			},
			new Zarafa.plugins.smime.data.SmimeResponseHandler({
				successCallback : this.onChangePassphraseRequest.createDelegate(this)
			})
		);
	},

	/**
	 * Handler for successCallback of the change passphrase change request.
	 */
	onChangePassphraseRequest: function(response) {
		if (response.code === Zarafa.plugins.smime.CHANGE_CERTIFICATE_SUCCESS) {
			container.getNotifier().notify('info.saved', _('S/MIME Message'), _('Passphrase changed successfully'));
			this.dialog.close();
		} else {
			this.wrong_passphrase.show();
		}
	}
});

Ext.reg('smime.changepassphrasepanel', Zarafa.plugins.smime.dialogs.ChangePassphrasePanel);
Ext.namespace('Zarafa.plugins.smime.dialogs');

/**
 * @class Zarafa.plugins.smime.dialogs.PassphraseContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 *
 * The content panel for changing a users passphrase.
 * @xtype smime.passphrasecontentpanel
 */
Zarafa.plugins.smime.dialogs.PassphraseContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {
	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};
		config = Ext.applyIf(config, {
			xtype: 'smime.passphrasecontentpanel',
			cls : 'zarafa-smime-passphrasewindow',
			modal: true,
			layout: 'fit',
			title : _('S/MIME Passphrase'),
			width: 350,
			height: 100,
			stateful: false,
			items: [{
				xtype: 'smime.passphrasewindow',
				ref: 'passphrasePanel',
				record: config.record
			}]
		});

		Zarafa.plugins.smime.dialogs.PassphraseContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('smime.passphrasecontentpanel', Zarafa.plugins.smime.dialogs.PassphraseContentPanel);
Ext.namespace('Zarafa.plugins.smime.dialogs');

/**
 * @class Zarafa.plugins.smime.dialogs.PassphraseWindow
 * @extends Zarafa.core.ui.ContentPanel
 *
 * The content panel which asks the user for his passphrase and verifies if it's correct.
 * @xtype smime.passphrasewindow
 */
Zarafa.plugins.smime.dialogs.PassphraseWindow = Ext.extend(Ext.Panel, {

	/**
	 * cfg {Ext.Button} btn the smime security dropdown button
	 */
	btn : undefined,

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

		this.btn = config.record;

		Ext.applyIf(config, {
			xtype: 'smime.passphrasewindow',
			layout: 'fit',
			border: false,
			items: this.getInnerItems(),
			buttons : [{
				type: 'submit',
				text: _('Submit'),
				cls: 'zarafa-action passphrase_submit',
				handler: this.checkPassphrase,
				scope: this
			},{
				text: _('Cancel'),
				cls: 'passphrase_cancel',
				handler: this.onCancel,
				scope: this
			}],
			listeners: {
				scope: this,
				beforedestroy: this.onBeforeDestroy
			}
		});

		Zarafa.plugins.smime.dialogs.PassphraseWindow.superclass.constructor.call(this, config);
	},

	/**
	 * Helper function which provide necessary inner items based on the detected browser.
	 * There are different expectations from browser to prompt for password.
	 * @return {Array} returns array of the inner items which will be rendered
	 */
	getInnerItems : function()
	{
		var innerItems = [];
		var passwordSaveEnabled = container.getSettingsModel().get('zarafa/v1/plugins/smime/passphrase_cache');

		if ( Ext.isGecko && passwordSaveEnabled ) {
			var url = container.getBaseURL();
			url = Ext.urlAppend(url, 'load=custom&name=smime_passphrase');

			innerItems.push({
				xtype : "box",
				autoEl : {
					tag : "iframe",
					style : "border-width: 0px; height:100%; width:100%",
					src: url
				},
				listeners : {
					render : this.onIframeRendered,
					scope : this
				}
			});
		} else {
			innerItems.push(this.createForm());
		}

		return innerItems;
	},

	/**
	 * Event handler for the 'destroy' event of the {@link Zarafa.plugins.smime.dialogs.PassphraseWindow}
	 * Will {@link Zarafa.core.BrowserWindowMgr.unRegister} the iframe from the
	 * {@link Zarafa.core.BrowserWindowMgr}
	 * @private
	 */
	onBeforeDestroy : function()
	{
		// If we created an iframe, we must unregister it from the BrowserWindowMgr
		if ( Ext.isDefined(this.windowName) ){
			var parentWindow = this.getEl().dom.ownerDocument.defaultView;
			Zarafa.core.BrowserWindowMgr.unRegister(this.windowName);
			Zarafa.core.BrowserWindowMgr.setActive(parentWindow.name);
		}
	},

	/**
	 * Event handler which is triggered when the user presses the Cancel button
	 * {@link Ext.Button}. This will close the {@link Zarafa.plugins.smime.dialogs.PassphraseWindow dialog}
	 * @private
	 */
	onCancel : function()
	{
		this.dialog.close();
	},

	/**
	 * Handler function which executes after the {@link Ext.Component} gets rendered.
	 * This will generate and add a form element along with necessary form-items into
	 * the underlying iframe element.
	 * @param {Ext.Component|HTMLElement} component which gets rendered into this window
	 */
	onIframeRendered : function(component) {
		var iframeElement = Ext.isDefined(component.getEl) ? component.el : component;

		Ext.EventManager.on(iframeElement, 'load', function(){

			// Create a unique name for the iframe window that can be used by the Zarafa.core.BrowserWindowMgr
			this.windowName = 'smime-passphrasewindow-'+Zarafa.plugins.smime.dialogs.PassphraseWindow.iframeCounter++;
			iframeElement.dom.contentWindow.name = this.windowName;
			Zarafa.core.BrowserWindowMgr.browserWindows.add(this.windowName, iframeElement.dom.contentWindow);
			Zarafa.core.BrowserWindowMgr.setActive(this.windowName);
			Zarafa.core.BrowserWindowMgr.initExtCss(iframeElement.dom.contentWindow);

			// Disable contextmenu globally in the iframe.
			Ext.getBody().on('contextmenu', Zarafa.core.BrowserWindowMgr.onBodyContextMenu, this);

			// Create a viewport for the iframe window that will take care of the resizing
			new Zarafa.plugins.smime.ui.Viewport({
				layout: 'fit',
				cls: 'k-smime-viewport',
				body: iframeElement.dom.contentDocument.body,
				items: [
					this.createForm()
				]
			});
		}, this);

	},

	/**
	 * Returns the config object for the {@link Ext.form.FormPanel} for
	 * the passphrase window.
	 */
	createForm : function()
	{
		var passwordSaveEnabled = container.getSettingsModel().get('zarafa/v1/plugins/smime/passphrase_cache');

		return {
			// We need a real form to trigger autofill on browsers, if the user does not want to
			// use autofill we will not use a form to avoid saving the password.
			xtype : passwordSaveEnabled ? 'smime.form' : 'panel',
			url : Ext.urlAppend(container.getBaseURL(), 'load=custom&name=smime_passphrasecheck'),
			method : 'POST',
			border: false,
			layout: 'form',
			items : [{
				xtype: 'textfield',
				inputType : 'text',
				cls: 'username',
				name : 'username',
				hidden : true,
				value : container.getUser().getSMTPAddress()
			},{
				xtype: 'textfield',
				inputType: 'password',
				cls: 'certificate_passphrase',
				defaultAutoCreate : {
					tag: 'input',
					type: 'password',
					size: '20',
					autocomplete: passwordSaveEnabled ? 'on' : 'nope',
					placeholder: _('Certificate passphrase')
				},
				name : 'spassword',
				fieldLabel : _('Certificate passphrase'),
				hideLabel : true,
				anchor : '100%',
				listeners : {
					scope: this,
					afterrender: this.onAfterRenderPasswordField,
					specialkey: this.onSpecialKey
				}
			},{
				// Firefox needs a submit button in the form to start autocomplete on submit,
				// so we add a hidden one.
				xtype: 'button',
				type: 'submit',
				hidden: true,
				ref: 'submitBtn'
			}],
			listeners : {
				scope: this,
				afterrender: function(panel){
					this.formPanel = panel;
				}
			}
		};
	},

	/**
	 * Event handler for the afterrender event of the password field. Will add
	 * the password, placeholder and autocomplete attributes to the password field
	 * @param {Ext.form.TextField} field The password field
	 */
	onAfterRenderPasswordField : function(field)
	{
		// Make sure the passphrase ref will point to the password field, even
		// if we use an iframe
		this.passphrase = field;

		// Focus the password field, so the user can hit the keys right away
		setTimeout(function() {
			field.focus();
		}, 500);

		// Activate the main browser window again, so the user can move the passphrase window
		var parentWindow = this.getEl().dom.ownerDocument.defaultView;
		Zarafa.core.BrowserWindowMgr.setActive(parentWindow.name);

		// Make sure we unregister the iframe window when the parent window is unloaded
		// (e.g when the popout window is closed)
		parentWindow.addEventListener('unload', function(){
			this.onBeforeDestroy();
		}.bind(this));
	},

	/**
	 * Function which checks if the user inputs an enter in the password textfield
	 * And then checks if a valid passphrase has been entered.
	 * @param {Ext.form.TextField} field
	 * @param {Ext.EventObject} eventobj
	 */
	onSpecialKey : function(field, eventobj)
	{
		if(eventobj.getKey() === eventobj.ENTER) {
			this.checkPassphrase(field);
		}
	},

	/**
	 * Function which calls a request to PHP which verifies if the supplied passphrase is correct.
	 * Calls onPassphraseCallback if there is a succesCallback.
	 */
	checkPassphrase : function() {
		var user = container.getUser();
		container.getRequest().singleRequest(
			'pluginsmimemodule',
			'passphrase',
			{
				'user' : user.getSMTPAddress(),
				'passphrase' : this.passphrase.getValue()
			},
			new Zarafa.plugins.smime.data.SmimeResponseHandler({
				successCallback : this.onPassphraseCallback.createDelegate(this)
			})
		);
	},

	/**
	 * successCallback function for the request to verify if a private certificate passphrase is correct
	 * If the response status is true, the contentpanel will be closed and the record message_class will be set
	 * and the record is saved.
	 * Otherwise the inputfield will be reset.
	 * @param {Object} response Json object containing the response from PHP
	 */
	onPassphraseCallback : function(response) {
		if(response.status) {
			if(this.btn instanceof Zarafa.core.data.IPMRecord) {
				this.btn.open({forceLoad: true});
			} else {
				var owner = this.btn.ownerCt;
				var record = owner.record;
				if (!record) {
					// This is the case where button belongs to the "more" menu.
					// Get the dialog from menu.
					var moreMenu = this.btn.parentMenu;
					var parentToolbar = moreMenu.ownerCt.ownerCt;
					record = parentToolbar.dialog.record;
				}
				record.set('message_class', this.btn.message_class);
				record.save();
				this.btn.setIconClass('icon_smime_sign_selected');
			}

			if ( this.formPanel.getXType() === 'smime.form' ){
				// Submit the form so the passphrase can be saved by FireFox. Submit by clicking
				// the hidden submit button in the form.
				this.formPanel.submitBtn.getEl().down('div').down('em').down('button').dom.click();
			}

			this.dialog.close();
		} else {
			if(this.passphrase) {
				this.passphrase.reset();
			}
			container.getNotifier().notify('error.connection', _('S/MIME Message'), _('The passphrase you typed is incorrect. Please try again.'));
		}
	}
});

Ext.reg('smime.passphrasewindow', Zarafa.plugins.smime.dialogs.PassphraseWindow);

// Counter that we use to give the iframe window a unique name
Zarafa.plugins.smime.dialogs.PassphraseWindow.iframeCounter = 0;
Ext.namespace('Zarafa.plugins.smime.ui');

/**
 * @class Zarafa.plugins.smime.ui.FormPanel
 * @extends Ext.FormPanel
 *
 * Extending the original {@link Ext.FormPanel} to be able to set the
 * action url of the form in the config.
 */
Zarafa.plugins.smime.ui.FormPanel = Ext.extend(Ext.FormPanel, {
	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor : function(config){
		config = config || {};
		Ext.apply(config, {
			xtype: 'smime.form'
		});

		Zarafa.plugins.smime.ui.FormPanel.superclass.constructor.call(this, config);
	},

	// private
	// Overridden to add the action attribute to the form
	initComponent : function()
	{
		Zarafa.plugins.smime.ui.FormPanel.superclass.initComponent.apply(this, arguments);

		Ext.applyIf(this.bodyCfg, {
			action: this.url
		});
	}
});

Ext.reg('smime.form', Zarafa.plugins.smime.ui.FormPanel);
Ext.namespace('Zarafa.plugins.smime.ui');

// Adding some functionality to the the Ext.EventManager so our viewport can handle the resizing of the iframe
Ext.apply(Ext.EventManager, function(){
	var resizeEvent;
	var resizeTask;
	var curHeight, curWidth;

	return {
		// private
		doIframeResizeEvent: function(iframeWindow){
			var doc = iframeWindow.document;
			var body = Ext.get(doc.body);

			var h = body.getHeight(),
				w = body.getWidth();

			//whacky problem in IE where the resize event will fire even though the w/h are the same.
			if(curHeight != h || curWidth != w){
				resizeEvent.fire(curWidth = w, curHeight = h);
			}
		},

		/**
		 * Adds a listener to be notified when the iframe window is resized and provides resize event buffering (100 milliseconds),
		 * passes new viewport width and height to handlers.
		 * @param {Function} fn	  The handler function the window resize event invokes.
		 * @param {Object}scopeThe scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
		 * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
		 */
		onIframeResize : function(fn, scope, options){
			if( resizeEvent ){
				resizeEvent.clearListeners();
			}

			resizeEvent = new Ext.util.Event();
			resizeTask = new Ext.util.DelayedTask(this.doIframeResizeEvent, this, [options.window]);
			Ext.EventManager.on(options.window, "resize", this.fireIframeResize, this);
			resizeEvent.addListener(fn, scope, options);
		},

		// exposed only to allow manual firing
		fireIframeResize : function(){
			if(resizeEvent){
				resizeTask.delay(100);
			}
		}
	};
}());

/**
 * @class Zarafa.plugins.smime.ui.Viewport
 * @extends Ext.Viewport
 *
 * The viewport we can use inside an iframe window. The original {@link Ext.Viewport}
 * needs some modifications to work on the correct document
 */
Zarafa.plugins.smime.ui.Viewport = Ext.extend(Ext.Viewport, {
	/**
	 * @cfg {Mixed} body The body element that this viewport will be attached to.
	 * Can be an HTML element or an {@link Ext.Element}
	 */
	body : null,

	initComponent : function() {
		Ext.Viewport.superclass.initComponent.call(this);

		if ( !(this.body instanceof Ext.Element) ){
			this.body = Ext.get(this.body);
		}
		var doc = this.body.dom.ownerDocument;
		var win = doc.defaultView;

		doc.getElementsByTagName('html')[0].className += ' x-viewport';
		this.el = this.body;
		this.el.setHeight = Ext.emptyFn;
		this.el.setWidth = Ext.emptyFn;
		this.el.setSize = Ext.emptyFn;
		this.el.dom.scroll = 'no';
		this.allowDomMove = false;
		this.autoWidth = true;
		this.autoHeight = true;
		Ext.EventManager.onIframeResize(this.fireResize, this, {window: win});
		this.renderTo = this.el;
	}
});
Ext.namespace('Zarafa.plugins.smime.settings');

/**
 * @class Zarafa.plugins.smime.settings.PublickeyGrid
 * @extends Ext.grid.GridPanel
 * @xtype smime.publickeygrid
 *
 * {@link Zarafa.plugins.smime.settings.PublickeyGrid PublickeyGrid} will be used to display
 * public certificates of the current user.
 */
Zarafa.plugins.smime.settings.PublickeyGrid = Ext.extend(Ext.grid.GridPanel, {
	/**
	 * @constructor
	 * @param {Object} config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};

		if(!config.store) {
			config.store = new Zarafa.plugins.smime.data.SmimeCertificateStore();
		}
	
		Ext.applyIf(config, {
			xtype : 'smime.publickeygrid',
			store : config.store,
			viewConfig : {
				forceFit : true,
                deferEmptyText : false,
				emptyText : '<div class=\'emptytext\'>' + _('No certificates imported, please upload your private certificate') + '</div>'
			},
			loadMask : this.initLoadMask(),
			columns : this.initColumnModel(),
			selModel : this.initSelectionModel(),
			listeners : {
				viewready : this.onViewReady,
				scope : this
			}
		});

		Zarafa.plugins.smime.settings.PublickeyGrid.superclass.constructor.call(this, config);
	},

	/**
	 * initialize events for the grid panel.
	 * @private
	 */
	initEvents : function()
	{
		Zarafa.plugins.smime.settings.PublickeyGrid.superclass.initEvents.call(this);

		// select first certificate when store has finished loading
		this.mon(this.store, 'load', this.onViewReady, this, {single : true});
	},

	/**
	 * Creates a column model object, used in {@link #colModel} config
	 * @return {Ext.grid.ColumnModel} column model object
	 * @private
	 */
	initColumnModel : function()
	{
		return [{
			dataIndex : 'email',
			header : _('Email'),
			sortable: true,
			renderer : Ext.util.Format.htmlEncode
		},{
			dataIndex : 'validfrom',
			sortable: true,
			header : _('Valid from'),
			renderer : Ext.util.Format.htmlEncode
		},{
			dataIndex : 'validto',
			sortable: true,
			header : _('Expires'),
			renderer : Ext.util.Format.htmlEncode
		},{
			dataIndex : 'type',
			sortable: true,
			header : _('Type'),
			renderer : Ext.util.Format.htmlEncode
		},{
			dataIndex : 'key_type',
			sortable: true,
			header : _('Key Type'),
			width : 90,
			renderer : function(value, meta, record) {
				var bits = record.get('key_bits');
				var curve = record.get('curve_name');
				if (value === 'EC' && curve) {
					return Ext.util.Format.htmlEncode(value + ' ' + curve);
				} else if (value === 'RSA' && bits > 0) {
					return Ext.util.Format.htmlEncode(value + ' ' + bits);
				}
				return Ext.util.Format.htmlEncode(value || 'unknown');
			}
		},{
			dataIndex : 'purpose',
			sortable: true,
			header : _('Purpose'),
			width : 80,
			renderer : function(value) {
				var labels = {
					'sign': _('Sign'),
					'encrypt': _('Encrypt'),
					'both': _('Sign & Encrypt'),
					'unknown': _('Unknown')
				};
				return labels[value] ? labels[value] : Ext.util.Format.htmlEncode(value);
			}
		}];
	},

	/**
	 * Creates a selection model object, used in {@link #selModel} config
	 * @return {Ext.grid.RowSelectionModel} selection model object
	 * @private
	 */
	initSelectionModel : function()
	{
		return new Ext.grid.RowSelectionModel({
			singleSelect : true
		});
	},

	/**
	 * Initialize the {@link Ext.grid.GridPanel.loadMask} field
	 *
	 * @return {Object} The configuration object for {@link Ext.LoadMask}
	 * @private
	 */
	initLoadMask : function()
	{
		return {
			msg : _('Loading certificates') + '...'
		};
	},

	/**
	 * Event handler which is fired when the gridPanel is ready. This will automatically
	 * select the first row in the grid.
	 * @private
	 */
	onViewReady : function()
	{
		this.getSelectionModel().selectFirstRow();
	},



	/**
	 * Function will be called to remove a certificate.
	 */
	removeCertificate : function()
	{
		var selectionModel = this.getSelectionModel();
		var certificate = selectionModel.getSelected();

		if(!certificate) {
			Ext.MessageBox.show({
				title: _('S/MIME Plugin'),
				msg: _('Please select a certificate.'),
				buttons: Ext.MessageBox.OK,
				icon: Ext.MessageBox.INFO
			});
			return;
		} else if(certificate.get('type') === 'private') {
			Ext.MessageBox.show({
				title: _('S/MIME Plugin'),
				msg :_('Do you really want to remove your private certificate? If you remove your certificate you will not be able to sign or decrypt S/MIME emails.'),
				icon: Ext.MessageBox.WARNING,
				fn: this.onRemoveCertificate,
				scope: this,
				buttons: Ext.MessageBox.YESNO
			});
		} else {
			this.onRemoveCertificateHelper();
		}
	},	
	
	/**
	 * Handler for removing private certificate dialog
	 * @param {String} btn 
	 */
	onRemoveCertificate : function(btn)
	{
		if(btn === 'yes') {
			this.onRemoveCertificateHelper();
		}
	},

	/**
	 * Helper function to avoid code duplication in onRemoveCertificate and removeCertificate
	 */
	onRemoveCertificateHelper : function()
	{
		var selectionModel = this.getSelectionModel();
		var certificate = selectionModel.getSelected();
		// before removing record we should select next available record,
		// because deleting record will remove selection
		if (selectionModel.hasNext()) {
			selectionModel.selectNext();
		} else if (selectionModel.hasPrevious()) {
			selectionModel.selectPrevious();
		}

		this.store.remove(certificate);
	},

	/**
	 * Function will be called to show details of a certificate
	 */
	showDetails : function()
	{
		var selectionModel = this.getSelectionModel();
		var certificate = selectionModel.getSelected();
		if (!Ext.isDefined(certificate))
			return;
		// TODO: use ExtJS form?
		
		var enc = Ext.util.Format.htmlEncode;
		var keyType = certificate.get('key_type') || 'unknown';
		var keyBits = certificate.get('key_bits') || 0;
		var curveName = certificate.get('curve_name') || '';
		var purpose = certificate.get('purpose') || 'both';

		var keyInfo = keyType;
		if (keyType === 'EC' && curveName) {
			keyInfo = keyType + ' ' + curveName;
		} else if (keyType === 'RSA' && keyBits > 0) {
			keyInfo = keyType + ' ' + keyBits + ' bits';
		}

		var purposeLabels = {
			'sign': _('Signing'),
			'encrypt': _('Encryption'),
			'both': _('Signing & Encryption'),
			'unknown': _('Unknown')
		};

		var text = _('Email') + ": " + enc(certificate.get('email')) + "<br>" +
			_('Serial') + ": " + enc(certificate.get('serial')) + "<br>" +
			_('Issued by') + ": " + enc(certificate.get('issued_by')) + "<br>" +
			_('Issued to') + ": " + enc(certificate.get('issued_to')) + "<br>" +
			_('Key Type') + ": " + enc(keyInfo) + "<br>" +
			_('Purpose') + ": " + (purposeLabels[purpose] || enc(purpose)) + "<br>" +
			_('SHA-256 Fingerprint') + ": " + enc(certificate.get('fingerprint_md5')) + "<br>" +
			_('SHA-1 Fingerprint') + ": " + enc(certificate.get('fingerprint_sha1')) + "<br>";

		if (keyType === 'RSA' && keyBits > 0 && keyBits < 2048) {
			text += "<br><b style='color:red'>" + _('Warning: RSA key size is below 2048 bits. Consider upgrading to a stronger certificate.') + "</b>";
		}

		Ext.Msg.alert(_('Certificate details'), text);
	}

});

Ext.reg('smime.publickeygrid', Zarafa.plugins.smime.settings.PublickeyGrid);
Ext.namespace('Zarafa.plugins.smime.settings');

/**
 * @class Zarafa.plugins.smime.settings.SettingsPublickeyPanel
 * @extends Ext.Panel
 * @xtype smime.publiccertificatespanel
 * Will generate UI for the {@link Zarafa.plugins.smime.settings.SettingsPublickeyWidget SettingsPublickeyWidget}.
 */
Zarafa.plugins.smime.settings.SettingsPublickeyPanel = Ext.extend(Ext.Panel, {
	/**
	 * @cfg {Zarafa.plugins.smime.data.SmimeCertificateStore} store Certificate store that will be used to load public certificates
	 */
	store : undefined,

	/**
	 * The LoadMask object which will be shown when the {@link #record} is being opened, and
	 * the dialog is waiting for the server to respond with the desired data.
	 * @property
	 * @type Zarafa.common.ui.LoadMask
	 */
	loadMask : undefined,

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

		if(!config.store) {
			config.store = new Zarafa.plugins.smime.data.SmimeCertificateStore();
		}

		Ext.applyIf(config, {
			xtype : 'smime.publiccertificatespanel',
			border : false,
			layout : {
				type : 'vbox',
				align : 'stretch',
				pack  : 'start'
			},
			items : this.createPanelItems(config.store)
		});

		Zarafa.plugins.smime.settings.SettingsPublickeyPanel.superclass.constructor.call(this, config);
	},

	/**
	 * Function will create panel items for {@link Zarafa.plugins.smime.data.SmimeCertificateStore SmimeCertificateStore}
	 * @return {Array} array of items that should be added to panel.
	 * @param {Zarafa.plugins.smime.data.SmimeCertificateStore} store store that will be used to load public certificates.
	 * @private
	 */
	createPanelItems : function(store)
	{
		var certStore = store;
		return [{
			xtype : 'container',
			flex : 1,
			layout : {
				type : 'hbox',
				align : 'stretch',
				pack  : 'start'
			},
			items : [{
				xtype : 'smime.publickeygrid',
				ref : '../publickeyGrid',
				store : store,
				flex : 1
			}, {
				xtype : 'container',
				width : 160,
				defaults : {
					width : 140
				},
				layout : {
					type : 'vbox',
					align : 'center',
					pack  : 'start'
				},
				items : [{
					xtype : 'displayfield',
					value : _('Filter on Email')
				}, {
					xtype : 'textfield',
					fieldLabel : _('Filter on Email'),
					hideLabel : true,
					listeners : {
						specialkey: function(f, e) {
							if(e.getKey() == e.ENTER){
								certStore.filter('email', f.getValue());
							}
						}
					}
				}, {
					xtype : 'spacer',
					height : 20
				}, {
					xtype : 'button',
					width : 84,
					text : _('Remove'),
					disabled : true,
					ref : '../../removeButton',
					handler : this.onCertificateRemove,
					scope : this
				}, {
					xtype : 'spacer',
					height : 20
				}, {
					xtype : 'button',
					width : 84,
					text : _('Details'),
					disabled : true,
					ref : '../../detailButton',
					handler : this.onDetailButton,
					scope : this

				}]
			}]
		}];
	},

	/**
	 * initialize events for the panel.
	 * @private
	 */
	initEvents : function()
	{
		Zarafa.plugins.smime.settings.SettingsPublickeyPanel.superclass.initEvents.call(this);

		// register event to enable/disable buttons
		this.mon(this.publickeyGrid.getSelectionModel(), 'selectionchange', this.onGridSelectionChange, this);
	},

	/**
	 * If {@link #showLoadMask} is enabled, this function will display the {@link #loadMask}.
	 * @protected
	 */
	showLoadMask : function()
	{
		if (!this.loadMask) {
			this.loadMask = new Zarafa.common.ui.LoadMask(this.el);
		}

		this.loadMask.show();
	},

	/**
	 * If {@link #showLoadMask} is enabled, and {@link #showLoadMask} has been
	 * called to display the {@link #loadMask} this function will disable the loadMask.
	 * @protected
	 */
	hideLoadMask : function()
	{
		if (this.loadMask) {
			this.loadMask.hide();
		}
	},

	/**
	 * Event handler will be called when selection in {@link Zarafa.plugins.smime.settings.PublickeyGrid PublickeyGrid}
	 * has been changed
	 * @param {Ext.grid.RowSelectionModel} selectionModel selection model that fired the event
	 */
	onGridSelectionChange : function(selectionModel)
	{
		var noSelection = (selectionModel.hasSelection() === false);

		this.removeButton.setDisabled(noSelection);
		this.detailButton.setDisabled(noSelection);
	},

	/**
	 * Handler function will be called when user clicks on 'Remove' button,
	 * this will remove currently selected certificate from certificates list.
	 * @private
	 */
	onCertificateRemove : function()
	{
		this.publickeyGrid.removeCertificate();
	},

	/**
	 * Handler function will be called when user clicks on 'Details' button,
	 * this will open the details dialog.
	 * @private
	 */
	onDetailButton : function()
	{
		this.publickeyGrid.showDetails();
	},

	/**
	 * Function will be used to reload data in the {@link Zarafa.plugins.smime.data.SmimeCertificateStore SmimeCertificateStore}
	 */
	discardChanges : function()
	{
		this.store.load();
	},

	/**
	 * Function will be used to save changes in the {@link Zarafa.plugins.smime.data.SmimeCertificateStore SmimeCertificateStore}
	 */
	saveChanges : function()
	{
		this.store.save();
	}
});

Ext.reg('smime.publiccertificatespanel', Zarafa.plugins.smime.settings.SettingsPublickeyPanel);
Ext.namespace('Zarafa.plugins.smime.settings');
/**
 * @class Zarafa.smime.settings.SettingsPublickeyWidget
 * @extends Zarafa.settings.ui.SettingsWidget
 * @xtype smime.settingspublickeysmimewidget
 *
 * The {@link Zarafa.settings.SettingsPublickeyWidget widget} for managing public certificates
 */
Zarafa.plugins.smime.settings.SettingsPublickeyWidget = Ext.extend(Zarafa.settings.ui.SettingsWidget, {
	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor : function(config) {
		config = config || {};

		Ext.applyIf(config, {
			height : 400,
			title : _('Public & Private certificates'),
			cls : 'zarafa-settings-widget k-settings-nogap',
			xtype : 'smime.settingspublickeysmimewidget',
			layout : {
				// override from SettingsWidget
				type : 'fit'
			},
			items : [{
				xtype : 'smime.publiccertificatespanel',
				ref : 'certificatesPanel',
				store: config.store
			}]
		});
		
		Zarafa.plugins.smime.settings.SettingsPublickeyWidget.superclass.constructor.call(this, config);
	},

	/**
	 * @return {Zarafa.public.smime.data.SmimeCertificateStore} The store which is holds all the public certificates
	 */
	getCertificateStore : function()
	{
		return this.certificatesPanel.store;
	},

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

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

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

		this.mon(this.getCertificateStore(), {
			'remove' : this.doStoreRemove,
			'update' : this.doStoreUpdate,
			scope : this
		});
	},

	/**
	 * Event handler for the {@link Ext.data.Store#remove} event which is fired
	 * by the {@link Ext.data.Store} inside the {@link #certificatePanel}.
	 * This will mark the {@link Zarafa.settings.SettingsContextModel} as
	 * {@link Zarafa.settings.SettingsContextModel#setDirty dirty}.
	 * @param {Ext.data.Store} store The store which fired the event
	 * @param {Ext.data.Record} record The record which was updated
	 * @private
	 */
	doStoreRemove : function(store, record)
	{
		if(!record.phantom) {
			this.settingsContext.getModel().setDirty();
		}
	},

	/**
	 * Event handler for the {@link Ext.data.Store#update} event which is fired
	 * by the {@link Ext.data.Store} inside the {@link #certificatePanel}.
	 * This will mark the {@link Zarafa.settings.SettingsContextModel} as
	 * {@link Zarafa.settings.SettingsContextModel#setDirty dirty}.
	 * @param {Ext.data.Store} store The store which fired the event
	 * @param {Ext.data.Record} record The record which was updated
	 * @param {String} operation The update operation being performed.
	 * @private
	 */
	doStoreUpdate : function(store, record, operation)
	{
		if (operation !== Ext.data.Record.COMMIT) {
			this.settingsContext.getModel().setDirty();
		}
	},

	/**
	 * Event handler will be called when {@link Zarafa.settings.SettingsContextModel#savesettings} event is fired.
	 * This will relay this event to {@link Zarafa.plugins.smime.settings.SettingsPublickeyPanel PublickeyPanel} so it can
	 * save certificates.
	 * @private
	 */
	onSaveSettings : function()
	{
		this.certificatesPanel.saveChanges();
	},

	/**
	 * Event handler will be called when {@link Zarafa.settings.SettingsContextModel#discardsettings} event is fired.
	 * This will relay this event to {@link Zarafa.plugins.smime.settings.SettingsPublicKeyPanel PublickeyPanel} so it can
	 * discard current changes and reload public certificates from the server.
	 * @private
	 */
	onDiscardSettings : function()
	{
		this.certificatesPanel.discardChanges();
	}
});

Ext.reg('smime.settingspublickeywidget', Zarafa.plugins.smime.settings.SettingsPublickeyWidget);
Ext.namespace('Zarafa.plugins.smime.settings');

/**
 * @class Zarafa.plugins.smime.settings.SettingsSmimeCategory
 * @extends Zarafa.settings.ui.SettingsCategory
 * @xtype smime.settingssmimecategory
 *
 * The smime category for users which will
 * allow the user to upload public/private certificates
 */
Zarafa.plugins.smime.settings.SettingsSmimeCategory = Ext.extend(Zarafa.settings.ui.SettingsCategory, {
        /**
         * @insert context.settings.category.smime
         * Insertion point to register new {@link Zarafa.settings.settingssmimewidget widgets}
         * for the {@link Zarafa.smime.settings.settingssmimecategory}.
         * @param {Zarafa.smime.settings.settingssmimecategory} category The smime
         * category to which the widgets will be added.
         */

        /**
         * @constructor
         * @param {Object} config Configuration object
         */
        constructor : function(config) {
                config = config || {};
		this.store = new Zarafa.plugins.smime.data.SmimeCertificateStore();

                Ext.applyIf(config, {
			title : _('S/MIME'),
                        categoryIndex : 1,
                        iconCls : 'icon_smime_settings',
                        items : [{
				xtype : 'smime.settingssmimewidget',
				store: this.store
                        },{
				xtype : 'smime.uploadcertificatewidget',
				store: this.store
                        },{
				xtype : 'smime.settingspublickeywidget',
				settingsContext : config.settingsContext,
				store: this.store
			}]
                });

                Zarafa.plugins.smime.settings.SettingsSmimeCategory.superclass.constructor.call(this, config);
        },

	/**
	 * Called by superclass when the Category has been deselected and is hidden from the user,
	 * this will unregister the {@link #onBeforeSaveRules} event handler.
	 * @private
	 */
	onHide : function()
	{
                Zarafa.plugins.smime.settings.SettingsSmimeCategory.superclass.onHide.apply(this, arguments);

		// Unregister the 'beforesave' event. This could be lingering when
		// 'savesettings' was fired but it was cancelled by one of the
		// event handlers.
		this.mun(this.store, 'beforesave', this.onBeforeSaveCertificate, this);
	},

	/**
	 * Event handler for the
	 * {@link Zarafa.settings.SettingsContextModel ContextModel}#{@link Zarafa.settings.SettingsContextModel#beforesavesettings beforesavesettings}
	 * event. It will reset the {@link #savingElCounter} and his will register the event handler for
	 * {@link Zarafa.settings.SettingsModel#beforesave beforesave} event.
	 * @private
	 */
	onBeforeSaveSettingsModel : function()
	{
		Zarafa.plugins.smime.settings.SettingsSmimeCategory.superclass.onBeforeSaveSettingsModel.apply(this, arguments);

		this.mon(this.store, 'beforesave', this.onBeforeSaveCertificate, this, { single : true });
	},
	
	/**
	 * Event handler which is fired when the {@link Zarafa.plugins.smime.data.SmimeCertificateStore SmimeCertificateStore}
	 * fires the 'beforesave' event. This will {@link #displaySavingMask show a notification} and register the
	 * event handlers for the completion of the save.
	 * @private
	 */
	onBeforeSaveCertificate : function()
	{
		this.displaySavingMask();

		this.mon(this.store, 'save', this.onCertificateSave, this);
	},

	/**
	 * Event handler which is fired when the {@link Zarafa.plugins.smime.data.SmimeCertificateStore SmimeCertificateStore}
	 * fires the 'save' event indicating the successful save of the delegates. This will
	 * {@link #hideSavingMask hide the notification}.
	 * @private
	 */
	onCertificateSave : function()
	{
		this.hideSavingMask(true);

		this.mun(this.store, 'save', this.onCertificateSave, this);
	}
});

Ext.reg('smime.settingssmimecategory', Zarafa.plugins.smime.settings.SettingsSmimeCategory); 
Ext.namespace('Zarafa.plugins.smime.settings');
/**
 * @class Zarafa.plugins.smime.settings.SettingsSmimeWidget
 * @extends Zarafa.settings.ui.SettingsWidget
 * @xtype smime.settingssmimewidget
 *
 * The {@link Zarafa.plugins.settings.SettingsSmimeWidget widget} for importing S/MIME certificates (public/private)
 */
Zarafa.plugins.smime.settings.SettingsSmimeWidget = Ext.extend(Zarafa.settings.ui.SettingsWidget, {

	/**
	 * @cfg {Zarafa.core.data.IPMRecord} record.
	 */
	record : undefined,

	/**
	 * The default button label for the 'Change passphrase' button
	 * @cfg {String} defaultButtonLabel
	 */
	defaultButtonLabel : _('You don\'t have a valid certificate corresponding to your account'),


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

		if(!config.store) {
			config.store = new Zarafa.plugins.smime.data.SmimeCertificateStore();
		}

		Ext.applyIf(config, {
			title	: _('Personal certificate'),
			layout : 'form',
			xtype : 'smime.settingssmimewidget',
			items :[{
				xtype: 'button',
				text: _('Change passphrase for last certificate'),
				labelStyle: 'width:580px',
				ref: 'certificateField',
				fieldLabel : this.defaultButtonLabel,
				labelSeparator: '',
				handler : this.changePassphrase
			},{
				xtype: 'combo',
				fieldLabel: _('Default encryption algorithm'),
				ref: 'cipherCombo',
				store: new Ext.data.ArrayStore({
					fields: ['value', 'display'],
					data: [
						['aes-256-gcm', 'AES-256-GCM (' + _('Recommended') + ')'],
						['aes-128-gcm', 'AES-128-GCM'],
						['aes-256-cbc', 'AES-256-CBC (' + _('Legacy') + ')'],
						['aes-128-cbc', 'AES-128-CBC (' + _('Legacy') + ')']
					]
				}),
				valueField: 'value',
				displayField: 'display',
				mode: 'local',
				triggerAction: 'all',
				editable: false,
				value: container.getSettingsModel().get('zarafa/v1/plugins/smime/default_cipher', 'aes-256-gcm'),
				listeners: {
					select: function(combo, record) {
						container.getSettingsModel().set('zarafa/v1/plugins/smime/default_cipher', record.get('value'));
					}
				}
			},{
				xtype: 'combo',
				fieldLabel: _('Default signing digest'),
				ref: 'digestCombo',
				store: new Ext.data.ArrayStore({
					fields: ['value', 'display'],
					data: [
						['sha256', 'SHA-256 (' + _('Recommended') + ')'],
						['sha384', 'SHA-384'],
						['sha512', 'SHA-512']
					]
				}),
				valueField: 'value',
				displayField: 'display',
				mode: 'local',
				triggerAction: 'all',
				editable: false,
				value: container.getSettingsModel().get('zarafa/v1/plugins/smime/default_digest', 'sha256'),
				listeners: {
					select: function(combo, record) {
						container.getSettingsModel().set('zarafa/v1/plugins/smime/default_digest', record.get('value'));
					}
				}
			}]
		});

		Zarafa.plugins.smime.settings.SettingsSmimeWidget.superclass.constructor.call(this, config);
	},


	/**
	 * initialize events for the grid panel.
	 * @private
	 */
	initEvents : function()
	{
		Zarafa.plugins.smime.settings.SettingsSmimeWidget.superclass.initEvents.call(this);
		this.mon(this.store, 'load', this.onStoreReady, this);
		this.mon(this.store, 'remove', this.onStoreReady, this);
		this.onStoreReady();
	},

	/**
	 * Event handler which is fired when the store is loaded or an item is removed from the store
	 * @private
	 */
	onStoreReady : function()
	{
		var index = this.store.findExact('type', 'private');
		if(index === -1) {
			this.certificateField.disable();
			this.setCertificateButtonLabel(this.defaultButtonLabel);
			return;
		}
		this.certificateField.enable();
		this.record = this.store.getAt(index);
		// TODO: add validity message
		this.setCertificateButtonLabel(_('You have a valid certificate corresponding to your account'));
	},

	/**
	 * Helper function to set the fieldLabel of the certificate field button.
	 * @param {String} text fieldLabel text to be set.
	 */
	setCertificateButtonLabel : function(text)
	{
		if (this.certificateField.rendered) {
			this.certificateField.label.update(text);
		} else {
			this.certificateField.fieldLabel = text;
		}
	},

	/**
	 * Handler for 'change passphrase' button, opens a panel which allows a
	 * user to change his certificate's passphrase.
	 */
	changePassphrase : function()
	{
		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['plugin.smime.dialog.changepassphrasecontentpanel'], undefined, {modal: true});
	}
});

Ext.reg('smime.settingssmimewidget', Zarafa.plugins.smime.settings.SettingsSmimeWidget);
Ext.namespace('Zarafa.plugins.smime.settings');
/*
 * #dependsFile plugins/smime/js/data/SmimeAttachmentStore.js
 */

/**
 * @class Zarafa.plugins.smime.settings.UploadCertificateWidget
 * @extends Zarafa.settings.ui.SettingsWidget
 * @xtype smime.uploadcertificatewidget
 *
 * The {@link Zarafa.plugins.settings.UploadCertificateWidget widget} for importing S/MIME certificates (public/private)
 */
Zarafa.plugins.smime.settings.UploadCertificateWidget = Ext.extend(Zarafa.settings.ui.SettingsWidget, {
	/**
	 * @cfg {Object} form used to temporarily store the value of form in 
	 * {@link #selectCertificateCallback} for {@link #uploadCertificate}.
	 */
	form: undefined,

	/**
	 * @cfg {Object} files used to temporarily store the value of form in 
	 * {@link #selectCertificateCallback for {@link #uploadCertificate}.
	 */
	files: undefined,

	/**
	 * @cfg {Zarafa.core.data.IPMRecord} record.
	 */
	record : undefined,

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

		if(!config.store) {
			config.store = new Zarafa.plugins.smime.data.SmimeCertificateStore();
		}

		Ext.applyIf(config, {
			title	: _('Upload your certificate'),
			layout : 'form',
			xtype : 'smime.uploadcertificatewidget',
			items :[{
				xtype: 'displayfield',
				hideLabel : true,
				value : _('Below you can upload your private certificate in PKCS#12 format. grommunio Web only accepts certificates which are valid and only stores one private certificate on the server.')
			},{
				xtype : 'button',
				text : _('Select'),
				defaultValue : _('Select'),
				iconCls : 'icon_smime_settings',
				fieldLabel : _('Private certificate'),
				ref : 'certificate',
				width: 84,
				handler : this.selectCertificate,
				scope : this
			},{
				xtype : 'textfield',
				inputType: 'password',
				fieldLabel : _('Certificate passphrase'),
				width: 200,
				ref : 'passphrase',
				scope : this
			},{
				xtype : 'button',
				text : _('Upload'),
				width : 84,
				handler : this.uploadCertificate,
				scope : this
			}]
		});

		Zarafa.plugins.smime.settings.UploadCertificateWidget.superclass.constructor.call(this, config);
	},

	/**
	 * Handler for {Ext.button} opens a attachment dialog where the user can select a certificate.
	 * Registers a callback function which receives the files and form.
	 */
	selectCertificate : function() 
	{
		this.record = Zarafa.core.data.RecordFactory.createRecordObjectByCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_SMIME, {});

		var attachComponent = new Zarafa.common.attachment.ui.UploadAttachmentComponent({
			callback : this.selectCertificateCallback,
			scope : this
		});

		attachComponent.openAttachmentDialog();
	},

	/**
	 * Handler for {Ext.button} 'upload', uploads certificate if a passphrase is filled in
	 * and certificate is selected.
	 */
	uploadCertificate : function()
	{
		if(Ext.isEmpty(this.passphrase.getValue())) {
			Ext.MessageBox.show({
				title: _('S/MIME Plugin'),
                                msg: _('You must fill in the certificate passphrase to upload your certificate.'),
                                buttons: Ext.MessageBox.OK,
                                icon: Ext.MessageBox.INFO
                        });
		} else if(Ext.isEmpty(this.files)) {
			Ext.MessageBox.show({
                                title: _('S/MIME Plugin'),
                                msg: _('You must first select a valid private certificate in PKCS#12 format.'),
                                buttons: Ext.MessageBox.OK,
                                icon: Ext.MessageBox.INFO
                        });
		} else {
			var attachmentStore = this.record.getAttachmentStore();
			this.mon(attachmentStore, 'update', this.onUpdate, this);
			var param = {sourcetype : 'certificate', passphrase: this.passphrase.getValue() };
			attachmentStore.uploadFiles(this.files, this.form, true, param);
		}
	},

	/**
	 * Callback function for {@link Zarafa.plugins.smime.settingssmimewidget.selectCertificate}.
	 * Function is used to set the files and form for {@link Zarafa.plugins.smime.settingsmimewidget.uploadCertificate}
	 * It also sets the certificate upload button's text to the selected filename.
	 * 
	 * @param {Object/Array} files The files is contains file information.
	 * @param {Object} form the form is contains {@link Ext.form.BasicForm bacisform} info.
	 */
	selectCertificateCallback : function(files, form)
	{
		this.certificate.setText(files[0].name);
		this.files = files;
		this.form = form;
	},

	/**
	 * Function for update event of {@link Zarafa.core.data.IPMAttachmentStore}.
	 * Displays the information which is stored in the {Ext.data.Record} fields called
	 * cert_warning and cert_message.
	 *
	 * @param {Ext.data.Store} store The {Ext.data.Store} where the record is stored.
	 * @param {Ext.data.record} record The {Ext.data.Record} from which the data was extracted.
	 */
	onUpdate : function(store, record)
	{
		if(record.get('cert')) {
			var message = record.get('cert_warning');
			// Show key type information when available
			var keyType = record.get('cert_key_type');
			var keyBits = record.get('cert_key_bits');
			var curveName = record.get('cert_curve_name');
			var purpose = record.get('cert_purpose');
			if (keyType) {
				var keyInfo = keyType;
				if (keyType === 'EC' && curveName) {
					keyInfo = keyType + ' ' + curveName;
				} else if (keyType === 'RSA' && keyBits > 0) {
					keyInfo = keyType + ' ' + keyBits + ' ' + _('bits');
				}
				var purposeLabels = {
					'sign': _('signing'),
					'encrypt': _('encryption'),
					'both': _('signing & encryption')
				};
				var purposeText = purposeLabels[purpose] || purpose || '';
				message += ' (' + keyInfo;
				if (purposeText) {
					message += ', ' + purposeText;
				}
				message += ')';
			}
			container.getNotifier().notify('info.saved', _('S/MIME Message'), message);
			this.store.load();
			this.passphrase.reset();
			this.certificate.setText(this.certificate.defaultValue);
		} else {
			container.getNotifier().notify('error.connection', _('S/MIME Message'), record.get('cert_warning'));
		}

	},

	/**
	 * Called by the {@link Zarafa.settings.ui.SettingsCategory Category} when
	 * it has been called with {@link zarafa.settings.ui.SettingsCategory#update}.
	 * This is being used to reset the textfield and button.
	 */
	update : function()
	{
		this.passphrase.reset();
		this.certificate.setText(this.certificate.defaultValue);
	}	
});

Ext.reg('smime.uploadcertificatewidget', Zarafa.plugins.smime.settings.UploadCertificateWidget);
Ext.namespace('Zarafa.plugins.smime');

/**
 * @class Zarafa.plugins.smime.SmimePlugin
 * @extends Zarafa.core.Plugin
 */
Zarafa.plugins.smime.SmimePlugin = Ext.extend(Zarafa.core.Plugin, {
	/*
	 * Called after constructor.
	 * Registers insertion points.
	 * @protected
	 */
	initPlugin : function()
	{
		Zarafa.plugins.smime.SmimePlugin.superclass.initPlugin.apply(this, arguments);

		// S/MIME button in mailcreatecontentpanel
		this.registerInsertionPoint('context.mail.mailcreatecontentpanel.toolbar.options', this.showSignButton, this);
		this.registerInsertionPoint('context.mail.mailcreatecontentpanel.toolbar.options', this.showEncryptButton, this);

		// S/MIME Settings widget insertion point
		this.registerInsertionPoint('context.settings.categories', this.createSettingsCategories, this);

		// Insertion point which shows the verified/decrypted status
		this.registerInsertionPoint('previewpanel.toolbar.detaillinks', this.showSmimeInfo, this);

		// S/MIME Icons
		this.registerInsertionPoint('context.mail.gridrow', this.showMessageClass , this);
		this.registerInsertionPoint('context.mail.griddefaultcolumn', this.showDefaultColumn, this);

		Zarafa.core.data.SharedComponentType.addProperty('plugin.smime.dialog.passphrasewindow');
		Zarafa.core.data.SharedComponentType.addProperty('plugin.smime.dialog.changepassphrasecontentpanel');
	},

	/**
	 * Create a category in settings for S/MIME
	 *
	 * @return {smimesettingscategory}
	 */
	createSettingsCategories : function(insertionName, settingsMainPanel, settingsContext)
	{
		return [{
			xtype : 'smime.settingssmimecategory',
			settingsContext: settingsContext
		}];
	},

	/**
	 * Displays S/MIME information in the previewpanel
	 *
	 * @return {Object} a box which on record update displays S/MIME information
	 */
	showSmimeInfo : function()
	{
		return {
			xtype: 'button',
			plugins : [ 'zarafa.recordcomponentupdaterplugin' ],
			autoEl: {
				tag: 'div',
				ref: 'smimeInfoBox'
			},
			scope : this,
			update : this.onSmimeInfo,
			handler : this.onSmimeButton
		};
	},

	/**
	 * Create button which sets the MessageClass to IPM.Note.deferSMIME.MultiPartSigned
	 * when creating an email.
	 * If certificate is unlocked we can display the Sign button, else a blank label
	 *
	 * @return {Config} creates a button for signing email
	 */
	showSignButton : function()
	{
		var defaultDigest = container.getSettingsModel().get('zarafa/v1/plugins/smime/default_digest', 'sha256');
		var groupId = 'smime-digest-' + Ext.id();
		return {
			xtype : 'splitbutton',
			text : _('Sign'),
			tooltip: {
				cls: 'smime-tooltip',
				title: _('Sign message'),
				text: _('Ensure the authenticity of the message by adding a digital signature to this message. Any changes to the message will invalidate the signature.')
			},
			iconCls : 'icon_smime_sign',
			listeners : {
				afterrender : this.onAfterRenderSmimeButton,
				beforeshow : this.onAfterRenderSmimeButton,
				scope : this
			},
			handler : this.onSignButton,
			scope : this,
			menu : {
				items : [{
					xtype : 'menucheckitem',
					text : 'SHA-256',
					digest : 'sha256',
					group : groupId,
					checked : defaultDigest === 'sha256',
					handler : this.onDigestSelect,
					scope : this
				},{
					xtype : 'menucheckitem',
					text : 'SHA-384',
					digest : 'sha384',
					group : groupId,
					checked : defaultDigest === 'sha384',
					handler : this.onDigestSelect,
					scope : this
				},{
					xtype : 'menucheckitem',
					text : 'SHA-512',
					digest : 'sha512',
					group : groupId,
					checked : defaultDigest === 'sha512',
					handler : this.onDigestSelect,
					scope : this
				}]
			}
		};
	},

	/**
	 * Create button which sets the MessageClass to IPM.Note.deferSMIME
	 * when creating an email in context.mail.mailcreatecontentpanel.toolbar.option
	 *
	 * @return {Object} creates a button for encrypting an email
	 */
	showEncryptButton : function()
	{
		var defaultCipher = container.getSettingsModel().get('zarafa/v1/plugins/smime/default_cipher', 'aes-256-gcm');
		var groupId = 'smime-cipher-' + Ext.id();
		return {
			xtype : 'splitbutton',
			text : _('Encrypt'),
			tooltip: {
				cls: 'smime-tooltip',
				title: _('Encrypt message'),
				text: _('Ensure the privacy of the message by encrypting its contents. Only the recipient of the message will be able to open it.')
			},
			iconCls : 'icon_smime_encrypt',
			listeners : {
				afterrender : this.onAfterRenderSmimeButton,
				beforeshow : this.onAfterRenderSmimeButton,
				scope : this
			},
			handler : this.onEncryptButton,
			scope : this,
			menu : {
				items : [{
					xtype : 'menucheckitem',
					text : 'AES-256-GCM',
					cipher : 'aes-256-gcm',
					group : groupId,
					checked : defaultCipher === 'aes-256-gcm',
					handler : this.onCipherSelect,
					scope : this
				},{
					xtype : 'menucheckitem',
					text : 'AES-128-GCM',
					cipher : 'aes-128-gcm',
					group : groupId,
					checked : defaultCipher === 'aes-128-gcm',
					handler : this.onCipherSelect,
					scope : this
				},{
					xtype : 'menucheckitem',
					text : 'AES-256-CBC (' + _('Legacy') + ')',
					cipher : 'aes-256-cbc',
					group : groupId,
					checked : defaultCipher === 'aes-256-cbc',
					handler : this.onCipherSelect,
					scope : this
				}]
			}
		};
	},

	/**
	 * Handler which is responsible to retain the icon class of sign and encrypt buttons,
	 * while opening a Draft.
	 * It just retrieve message_class from respective record and set the icon-class accordingly.
	 *
	 * @param {Ext.button} button Which just gets rendered
	 */
	onAfterRenderSmimeButton : function(button)
	{
		var dialog = this.getRespectiveDialog(button);
		var record = dialog.record;
		switch(record.get('message_class')) {
		case 'IPM.Note.deferSMIME':
			if (button.iconCls === 'icon_smime_encrypt') {
				button.setIconClass('icon_smime_encrypt_selected');
			}
			break;
		case 'IPM.Note.deferSMIME.MultipartSigned':
			if (button.iconCls === 'icon_smime_sign') {
				button.setIconClass('icon_smime_sign_selected');
			}
			break;
		case 'IPM.Note.deferSMIME.SignedEncrypt':
			if (button.iconCls === 'icon_smime_sign') {
				button.setIconClass('icon_smime_sign_selected');
			} else {
				button.setIconClass('icon_smime_encrypt_selected');
			}
			break;
		}
	},

	/**
	 * Handler for the button which is displayed when a encrypted / signed message is opened
	 * When an encrypted message is opened, we will send a request to unlock the certificate.
	 * When an signed email is opened, we will show a popup with extra information about the signed message
	 */
	onSmimeButton: function(button)
	{
		var smimeInfo = button.record.get('smime');
		if (!button.record.isOpened() || !smimeInfo)
			return;
		switch (smimeInfo.type) {
		case 'encrypted':
			if (smimeInfo.success === Zarafa.plugins.smime.SMIME_STATUS_GOOD)
				break;
			var user = container.getUser();
			container.getRequest().singleRequest(
				'pluginsmimemodule',
				'certificate',
				{
					'user' : user.getSMTPAddress()
				},
				new Zarafa.plugins.smime.data.SmimeResponseHandler({
					successCallback : this.onCertificateCallback.createDelegate(button.record)
				})
			);
			break;
		case 'signed':
		case 'encryptsigned':
			var popupText = Zarafa.plugins.smime.SmimeText.getPopupText(smimeInfo.info);
			var algoText = Zarafa.plugins.smime.SmimeText.formatAlgorithms(smimeInfo.algorithms);
			if (algoText) {
				popupText += '<br><br><b>' + _('Algorithms used') + '</b><br>' + algoText;
			}
			container.getNotifier().notify(Zarafa.plugins.smime.SmimeText.getPopupStatus(smimeInfo.success),
					_('What\'s going on with my email?'), popupText);
			break;
		}
	},

	/**
	 * Function which displays information in the previewpanel.
	 * In the case of a encrypted message, we either show a button to unlock the certificate
	 * or shows the message that the message has been decrypted.
	 * If the message is signed we display information depending on the state of the verification.
	 *
	 * @param {Zarafa.core.data.IPMRecord} record record which is displayed
	 */
	onSmimeInfo : function(record)
	{
		// Set button.record for use in onSmimeButton
		this.record = record;
		var smimeInfoBox = this.getEl();

		// Set smimeBox to empty value by default, to override previous S/MIME message text
		smimeInfoBox.update("");
		smimeInfoBox.removeClass('smime-info-good');
		smimeInfoBox.removeClass('smime-info-fatal');
		smimeInfoBox.removeClass('smime-info-partial');
		smimeInfoBox.removeClass('smime-info-info');

		if (!record) {
			return;
		}

		// retrieve smime json object
		var smimeInfo = record.get('smime');
		if (!record.isOpened() || !smimeInfo)
			return;
		var sender = record.getSender();

		// FIXME: refactor success, to status since that's probably a better description of the variable
		smimeInfoBox.addClass(Zarafa.plugins.smime.SmimeText.getStatusMessageClass(smimeInfo.success));
		var message = Zarafa.plugins.smime.SmimeText.getMessageInfo(smimeInfo.info);
		var algoTag = Zarafa.plugins.smime.SmimeText.formatAlgorithmTag(smimeInfo.algorithms);
		var isDecryptedSuccessfully = (smimeInfo.info === Zarafa.plugins.smime.SMIME_DECRYPT_SUCCESS);
		switch (smimeInfo.type) {
		case 'encrypted':
			// Empty smimeInfoBox
			if (smimeInfo.success === Zarafa.plugins.smime.SMIME_STATUS_BAD) {
				smimeInfoBox.update('<div class="icon_smime_encr_content"></div> ' + message);
				break;
			}
			if (isDecryptedSuccessfully) {
				var efailNote = '';
				if (smimeInfo.efail_warning) {
					efailNote = ' <span class="smime-efail-note">(' + _('CBC mode') + ')</span>';
				}
				// Decrypted messages are implicitly verified — show both badges
				smimeInfoBox.update(String.format('{0} &lt{1}&gt <div class="icon_smime_sign_content"></div><div class="icon_smime_decr_content"></div> {2}{3}{4}', sender.get('display_name'), sender.get('smtp_address'), message, efailNote, algoTag));
			} else {
				smimeInfoBox.update(String.format('{0} &lt{1}&gt <div class="icon_smime_encr_content"></div> {2}{3}', sender.get('display_name'), sender.get('smtp_address'), message, algoTag));
			}
			// Force the Attachmentlinks component to update, to view the attachments
			this.ownerCt.findByType('zarafa.attachmentlinks')[0].update(record, true);
			break;
		case 'signed':
			smimeInfoBox.update(String.format('{0} &lt{1}&gt <div class="icon_smime_sign_content"></div> {2}{3}', sender.get('display_name'), sender.get('smtp_address'), message, algoTag));
			break;
		case 'encryptsigned':
			smimeInfoBox.update(String.format('{0} &lt{1}&gt <div class="icon_smime_sign_content"></div> <div class="icon_smime_decr_content"></div> {2}{3}', sender.get('display_name'), sender.get('smtp_address'), message, algoTag));
			if (smimeInfo.success !== Zarafa.plugins.smime.SMIME_STATUS_BAD &&
			smimeInfo.success !== Zarafa.plugins.smime.SMIME_STATUS_PARTIAL) {
				// Force the Attachmentlinks component to update, to view the attachments
				this.ownerCt.findByType('zarafa.attachmentlinks')[0].update(record, true);
			}
			break;
		}
	},

	/**
	 * Function which collects all the recipients smtp_addresses in a list and sets them as 'smime'
	 * property in the mailrecord. Because the hook in PHP doesn't have updated recipienttable yet.
	 * Always append the logged in user, so the user is able to to view the sent encrypted mail in
	 * 'Sent Items'.
	 *
	 * @param {Zarafa.mailcreatecontentpanel} dialog
	 * @param {Zarfa.core.data.IPMRecord} record The record which is going to be send
	 * @return {Boolean} returns false if public key isn't find and stops the record from being send
	 *
	 */
	onBeforeSendRecord : function(dialog, record) {
		// Always append the currently logged in user.
		var user = container.getUser();
		var myself = {
			email: user.getSMTPAddress(),
			internal: true,
			username: container.getUser().getEmailAddress()
		};
		var recipients = [myself];

		var recipientStore = record.getRecipientStore();
		recipientStore.each(function(recip) {
			const addressType = recip.get('address_type');
			recipients.push(
				{
					email: recip.get('smtp_address'),
					internal: addressType === "ZARAFA" || addressType === "EX",
					username: recip.get('email_address')
				}
			);
		}, this);
		dialog.record.set('smime', recipients);

		// Pass algorithm selections to the backend
		var cipher = record.get('smime_cipher') || container.getSettingsModel().get('zarafa/v1/plugins/smime/default_cipher', '');
		var digest = record.get('smime_digest') || container.getSettingsModel().get('zarafa/v1/plugins/smime/default_digest', '');
		if (cipher) {
			dialog.record.set('smime_cipher', cipher);
		}
		if (digest) {
			dialog.record.set('smime_digest', digest);
		}

		return true;
	},

	/**
	 * Handler for the encrypt button, when clicked it sets the message_class to encrypted.
	 * If we press the button again we reset the message_class. When a user already has signed an email
	 * we set a special message_class on it to sign+encrypt
	 *
	 * @param {Ext.button} button
	 */
	onEncryptButton : function(button)
	{
		var dialog = this.getRespectiveDialog(button);
		var record = dialog.record;
		if (!record)
			return;

		switch (record.get('message_class')) {
		// Unselecting encrypt functionality
		case 'IPM.Note.deferSMIME':
		case 'IPM.Note.deferSMIME.SignedEncrypt':
			if (record.get('message_class') === 'IPM.Note.deferSMIME.SignedEncrypt') {
				record.set('message_class', 'IPM.Note.deferSMIME.MultipartSigned');
			} else {
				record.set('message_class', 'IPM.Note');
			}

			button.setIconClass('icon_smime_encrypt');
			// Reset smime property
			record.set('smime','');
			// Reset send action, otherwise the saveRecord will trigger a send when the user deselects encryption
			record.actions = {};

			// Remove event
			dialog.un('beforesendrecord', this.onBeforeSendRecord ,this);
			dialog.saveRecord();
			break;

		// We want to encrypt (possibly also sign + encrypt)
		case 'IPM.Note.deferSMIME.MultipartSigned':
		case 'IPM.Note':
			var plugin = this;
			var user = container.getUser();
			var newClass = record.get('message_class') === 'IPM.Note' ?
				'IPM.Note.deferSMIME' : 'IPM.Note.deferSMIME.SignedEncrypt';

			container.getRequest().singleRequest(
				'pluginsmimemodule',
				'certificate',
				{
					'user' : user.getSMTPAddress()
				},
				new Zarafa.plugins.smime.data.SmimeResponseHandler({
					successCallback: function(response) {
						plugin.onEncryptCertificateCallback(dialog, button, newClass, response);
					}
				})
			);
			break;
		}
	},

	/**
	* successCallback function for the request to verify if a certificate exists for encryption.
	* If the certificate exists we can set the message_class and icon, otherwise notify the user.
	*
	* @param {Zarafa.mailcreatecontentpanel} dialog
	* @param {Ext.button} button
	* @param {String} messageClass
	* @param {Object} response Json object containing the response from PHP
	*/
	onEncryptCertificateCallback : function(dialog, button, messageClass, response) {
		if (response.status) {
			var record = dialog.record;
			record.set('message_class', messageClass);
			button.setIconClass('icon_smime_encrypt_selected');
			dialog.on('beforesendrecord', this.onBeforeSendRecord ,this);
			dialog.saveRecord();
		} else {
			container.getNotifier().notify('info.saved', _('S/MIME Message'), response.message);
		}
	},

	/**
	 * Handler for the sign button, when clicked it checks if the private certificate exists.
	 * If we have signing already set and click it again, we unset it.
	 * If we already set have encryption set, we set a special message_class for both sign+ecnrypt.
	 *
	 * @param {Ext.button} button
	 */
	onSignButton : function(button)
	{
		var dialog = this.getRespectiveDialog(button);
		var record = dialog.record;
		if (!record) {
			dialog.saveRecord();
			return;
		}
		var plugin = this;
		var user = container.getUser();
		var request = function() {
			container.getRequest().singleRequest(
				'pluginsmimemodule',
				'certificate',
				{
					'user' : user.getSMTPAddress()
				},
				new Zarafa.plugins.smime.data.SmimeResponseHandler({
					successCallback : plugin.onCertificateCallback.createDelegate(button)
				})
			);
		};
		switch (record.get('message_class')) {
		// We want to sign
		case 'IPM.Note':
			button.message_class = 'IPM.Note.deferSMIME.MultipartSigned';
			request();
			break;
		// Encrypt + Sign
		case 'IPM.Note.deferSMIME':
			button.message_class = 'IPM.Note.deferSMIME.SignedEncrypt';
			request();
			break;
		case 'IPM.Note.deferSMIME.MultipartSigned':
			button.setIconClass('icon_smime_sign');
			record.set('message_class', 'IPM.Note');
			break;
		case 'IPM.Note.deferSMIME.SignedEncrypt':
			button.setIconClass('icon_smime_sign');
			record.set('message_class', 'IPM.Note.deferSMIME');
			break;
		}
		dialog.saveRecord();
	},

	/**
	 * successCallback function for the request to verify if a private certificate exists in the mapi userstore.
	 * If the response status is true the certificate exists and we will query the user for his passphrase.
	 *
	 * @param {Object} response Json object containing the response from PHP
	 */
	onCertificateCallback : function(response) {
		// TODO: improve functionality with less callbacks
		var btn = this;
		if(response.status) {
			Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['plugin.smime.dialog.passphrasewindow'], btn, {manager: Ext.WindowMgr});
		} else {
			container.getNotifier().notify('info.saved', _('S/MIME Message'), response.message);
		}
	},

	/*
	 * Bid for the type of shared component
	 * and the given record.
	 * This will bid on calendar.dialogs.importevents
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @return {Number} The bid for the shared component
	 */
	bidSharedComponent : function(type) {
		var bid = -1;

		switch(type) {
		case Zarafa.core.data.SharedComponentType['plugin.smime.dialog.passphrasewindow']:
			bid = 1;
			break;
		case Zarafa.core.data.SharedComponentType['plugin.smime.dialog.changepassphrasecontentpanel']:
			bid = 1;
			break;
		}
		return bid;
	},

	/**
	 * Will return the reference to the shared component.
	 * Based on the type of component requested a component is returned.
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @return {Ext.Component} Component
	 */
	getSharedComponent : function(type) {
		var component;

		switch(type) {
		case Zarafa.core.data.SharedComponentType['plugin.smime.dialog.passphrasewindow']:
			component = Zarafa.plugins.smime.dialogs.PassphraseContentPanel;
			break;
		case Zarafa.core.data.SharedComponentType['plugin.smime.dialog.changepassphrasecontentpanel']:
			component = Zarafa.plugins.smime.dialogs.ChangePassphraseContentPanel;
			break;
		}

		return component;
	},

	/**
	 * Shows the message class icon of signed or encrypted email in the defaultcolumn
	 */
	showDefaultColumn : function()
	{
		return  {
			header : '<p class="icon_smime_settings">&nbsp;<span class="title">' + '&nbsp' + _('S/MIME Message') + '</span></p>',
			headerCls: 'zarafa-icon-column',
			dataIndex : 'message_class',
			width : 24,
			sortable : false,
			renderer :  function(value, p, record) {
				var messageClass = record.get('message_class');
				if (messageClass === 'IPM.Note.SMIME' ||
				    messageClass === 'IPM.Note.deferSMIME' ||
				    messageClass === 'IPM.Note.SMIME.SignedEncrypt' ||
				    messageClass === 'IPM.Note.deferSMIME.SignedEncrypt') {
					p.css = 'icon_smime_encrypt';
				} else if (messageClass === 'IPM.Note.SMIME.MultipartSigned' ||
				    messageClass === 'IPM.Note.deferSMIME.MultipartSigned') {
					p.css = 'icon_smime_sign';
				}
				return '';
			},
			fixed : true,
			tooltip : _('S/MIME Message')
		};
	},

	/**
	 * Shows the message class icon of signed or encrypted or signed + encrypted email in the non defaultcolumn
	 *
	 * @param {string} insertionPoint name of insertion point
	 * @param {Zarafa.core.data.IPMRecord} record The record of a row
	 * @return {string} column entry
	 *
	 */
	showMessageClass : function(insertionPoint, record) {
		var messageClass = record.get('message_class');
		var icon = "";
		if (messageClass === 'IPM.Note.SMIME' ||
		    messageClass === 'IPM.Note.deferSMIME' ||
		    messageClass === 'IPM.Note.SMIME.SignedEncrypt' ||
		    messageClass === 'IPM.Note.deferSMIME.SignedEncrypt') {
			icon = 'icon_smime_encrypt';
		} else if (messageClass === 'IPM.Note.SMIME.MultipartSigned' ||
		    messageClass === 'IPM.Note.deferSMIME.MultipartSigned') {
			icon = 'icon_smime_sign';
		}

		return String.format('<td style="width: 24px"><div class="grid_compact {0}" style="height: 24px; width: 24px;">{1}</div></td>', icon, "");
	},

	/**
	 * Handler for digest algorithm selection from the split button menu.
	 * Stores the selection and enables signing if not already active.
	 *
	 * @param {Ext.menu.CheckItem} item the selected menu item
	 */
	onDigestSelect : function(item)
	{
		var dialog = this.getRespectiveDialog(item);
		if (!dialog || !dialog.record) {
			return;
		}
		dialog.record.set('smime_digest', item.digest);

		// Enable signing if not already active
		var mc = dialog.record.get('message_class');
		if (mc !== 'IPM.Note.deferSMIME.MultipartSigned' && mc !== 'IPM.Note.deferSMIME.SignedEncrypt') {
			var splitBtn = item.parentMenu.ownerCt;
			this.onSignButton(splitBtn);
		}
	},

	/**
	 * Handler for cipher algorithm selection from the split button menu.
	 * Stores the selection and enables encryption if not already active.
	 *
	 * @param {Ext.menu.CheckItem} item the selected menu item
	 */
	onCipherSelect : function(item)
	{
		var dialog = this.getRespectiveDialog(item);
		if (!dialog || !dialog.record) {
			return;
		}
		dialog.record.set('smime_cipher', item.cipher);

		// Enable encryption if not already active
		var mc = dialog.record.get('message_class');
		if (mc !== 'IPM.Note.deferSMIME' && mc !== 'IPM.Note.deferSMIME.SignedEncrypt') {
			var splitBtn = item.parentMenu.ownerCt;
			this.onEncryptButton(splitBtn);
		}
	},

	/**
	 * Helper function to retrieve dialog.
	 *
	 * @param {Ext.button} button Which just gets rendered
	 * @return {Zarafa.mailcreatecontentpanel} dialog which contains the button passed as parameter
	 */
	getRespectiveDialog : function(button) {
		var parentToolbar = false;

		if (button.ownerCt instanceof Zarafa.core.ui.Toolbar) {
			parentToolbar = button.ownerCt;
		} else if (button.parentMenu) {
			// This handles split button menu items and "more" menu items.
			// Walk up: menu item -> menu -> split button/menu button -> toolbar
			var menuOwner = button.parentMenu.ownerCt;
			if (menuOwner instanceof Zarafa.core.ui.Toolbar) {
				parentToolbar = menuOwner;
			} else if (menuOwner && menuOwner.ownerCt instanceof Zarafa.core.ui.Toolbar) {
				parentToolbar = menuOwner.ownerCt;
			} else if (menuOwner && menuOwner.ownerCt && menuOwner.ownerCt.ownerCt) {
				parentToolbar = menuOwner.ownerCt.ownerCt;
			}
		}
		return parentToolbar ? parentToolbar.dialog : false;
	}
});

// Add property to record to MailRecord, where extra information is stored about S/MIME messages
Zarafa.core.data.RecordFactory.addFieldToMessageClass('IPM.Note', [
	{name: 'smime', defaultValue: ''},
	{name: 'smime_cipher', type: 'string', defaultValue: ''},
	{name: 'smime_digest', type: 'string', defaultValue: ''}
]);

Zarafa.onReady(function() {
	Zarafa.plugins.smime.SMIME_STATUS_GOOD = 0;
	Zarafa.plugins.smime.SMIME_STATUS_PARTIAL = 1;
	Zarafa.plugins.smime.SMIME_STATUS_BAD = 2;
	Zarafa.plugins.smime.SMIME_DECRYPT_SUCCESS = 6;
	Zarafa.plugins.smime.SMIME_STATUS_INFO = 3;
	Zarafa.plugins.smime.CHANGE_CERTIFICATE_SUCCESS = 1;
	Zarafa.plugins.smime.CHANGE_CERTIFICATE_ERROR = 2;
	Zarafa.plugins.smime.CHANGE_CERTIFICATE_WRONG = 3;

	container.registerPlugin(new Zarafa.core.PluginMetaData({
		name : 'smime',
		displayName : _('S/MIME Plugin'),
		pluginConstructor : Zarafa.plugins.smime.SmimePlugin
	}));
});
Ext.namespace('Zarafa.plugins.smime');

/**
 * @class Zarafa.plugins.smime.SmimeText
 * Singleton which holds two functions with translation messages for certain status codes from PHP for S/MIME messages
 * One helper function to create a message.
 * @singleton
 */
Zarafa.plugins.smime.SmimeText = function () {
	return {
		/*
		 * Helper function to create the popup body message
		 *
		 * @param {String} text
		 * @return {String} popup text
		 */
		createMessage: function (text) {
			return _('grommunio Web can verify digital signatures of emails. A successful verification reassures you that the message has not been tampered with and validates the identity of the sender.') +
				"<br><br>" + _('You are seeing this message because the verification of the digital signature') + "<b>" + _(' has failed') + "</b>" + _(' for this message.') + "<br>" +
				"<br><b>" + _('What caused this issue?') + "</b><br><br>" +
				text +
				"<br><br><b>" + _('What should I do?') + "</b><br><br>" +
				_('You can continue to work. However, you should keep in mind that the identity and the authenticity of the message content could not be verified.') +
				"<br><br>" + _('You can contact your helpdesk or system administrator if you are not sure what to do next.');
		},

		/**
		 * S/MIME Popup text
		 * Function which returns the text for the popup which is drawn when a users clicks on the smimeInfo button
		 *
		 * @param {Number} index status code from PHP
		 * @return {String} popup text
		 */
		getPopupText: function (index) {
			switch (index) {
			// Verified successfully
			case 0:
				return _('grommunio Web can verify digital signatures of emails. A successful verification reassures you ') +
					_('that the message has not been tampered with and validates the identity of the sender.') + "<br><br>" +
					_('The verification of the digital signature was successful for this email message.');
			// Could not verify, missing public certificate
			case 1:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('grommunio Web could not find a public certificate for the recipient.'));
			// Signature verified, but certificate expired
			case 2:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The identity of the sender and authenticity of the message content have been verified, but the certificate used to sign the message expired on [d-m-Y].'));
			// Signature could not be verified
			case 3:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The digital signature could not be verified for unknown reasons.'));
			// Signature has been revoked
			case 4:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The digital certificate used to sign this message has been revoked (i.e. the sender has marked it as compromised)'));
			// The verification step with the Certificate Authority failed
			case 5:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The verification service of the certificate authority that signed the sender\'s certificate is not available. The validity of the certificate could not be verified.'));
			// Certificate does not support OCSP
			case 9:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The revocation status of the digital certificate used to sign this email is unknown (Server is unavailable or certificate does not support OCSP). The validity of the certificate could not be verified.'));
			// OCSP check disabled
			case 10:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The revocation status of the digital certificate used to sign this email is disabled (OCSP). The validity of the certificate could not be verified.'));
			// OCSP server offline
			case 11:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The certificate verification server (OCSP) is temporarily offline.'));
			// User
			case 13:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('Sender is removed from the server.'));
			// CRL revoked
			case 14:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The digital certificate used to sign this message has been revoked according to the Certificate Revocation List (CRL).'));
			// CRL unavailable
			case 15:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The revocation status of the certificate could not be verified because the CRL Distribution Point is unavailable.'));
			// Weak RSA key
			case 16:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The RSA key size used in this certificate is below the recommended minimum of 2048 bits. Consider upgrading to a stronger certificate.'));
			// Key usage mismatch
			case 17:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The certificate\'s Key Usage extension does not permit this operation. The certificate may be intended for signing only or encryption only.'));
			// EFAIL CBC warning
			case 18:
				return _('This message was encrypted using CBC mode (not authenticated encryption). While it was decrypted successfully, AES-GCM (authenticated encryption) provides stronger protection against modification attacks. Consider asking the sender to upgrade their S/MIME configuration.');
			// Signing time skew
			case 19:
				return Zarafa.plugins.smime.SmimeText.createMessage(_('The signing time in the digital signature differs significantly from the expected time. This may indicate a clock synchronization issue or message tampering.'));
			default:
				return '';
			}
		},

		/**
		 * Function to retrieve the status messages for the corresponding status code from PHP
		 * The translated text is drawn in the S/MIME previewpanel button
		 *
		 * @param {Number} index
		 * @return {String} text 
		 */
		getMessageInfo: function (index) {
			switch (index) {
			case 0:
				return _('Signature verified successfully');
			case 1:
				return _('Could not verify signature, missing public certificate');
			case 2:
				return _('Signature verification successful, but certificate is expired');
			case 3:
				return _('Signature could not be verified');
			case 4:
				return _('Certificate has been revoked');
			case 5:
				return _('Verification with Certificate Authority failed');
			case 6:
				return _('Message decrypted successfully');
			case 7:
				return _('Message decryption failed');
			case 8:
				return _('Please') + '<b> ' + _('click here') + ' </b> ' + _('to unlock your certificate');
			case 9:
				return _('Cannot determine revocation status of certificate');
			case 10:
				return _('Signature verified');
			case 11:
				return _('Cannot determine revocation status of certificate');
			case 12:
				return _('Unable to decrypt this message. Certificate does not match');
			case 13:
				return _('Verification failed. User is removed from the server.');
			case 14:
				return _('Certificate has been revoked (CRL)');
			case 15:
				return _('Cannot check revocation status (CRL unavailable)');
			case 16:
				return _('RSA key size below recommended minimum');
			case 17:
				return _('Certificate not valid for this operation (key usage mismatch)');
			case 18:
				return _('Decrypted (CBC mode - consider AES-GCM for stronger protection)');
			case 19:
				return _('Signing time differs from expected time');
			default:
				return '';
			}
		},

		/**
		 * Function which returns the css class for the corresponding error code
		 *
		 * @param {Number} index
		 * @return {String} text 
		 */
		getStatusMessageClass: function (index) {
			switch (index) {
			case 0:
				return 'smime-info-good';
			case 1:
				return 'smime-info-partial';
			case 2:
				return 'smime-info-fatal';
			case 3:
				return 'smime-info-info';
			default:
				return 'smime-info-info';
			}
		},

		/**
		 * Function which returns the popup status for the corresponding error code
		 *
		 * @param {Number} index
		 * @return {String} text 
		 */
		getPopupStatus: function (index) {
			switch (index) {
			case 0:
				return '';
			case 1:
				return 'warning';
			case 2:
				return 'error';
			default:
				return '';
			}
		},

		/**
		 * Format algorithm details for the popup dialog (verbose, multi-line).
		 *
		 * @param {Object} algos algorithm details from the backend (may be undefined)
		 * @return {String} formatted HTML string, or empty string if no data
		 */
		formatAlgorithms: function (algos) {
			if (!algos) {
				return '';
			}
			var parts = [];
			if (algos.digest) {
				parts.push(_('Digest') + ': ' + algos.digest.toUpperCase());
			}
			if (algos.signature) {
				parts.push(_('Signature') + ': ' + algos.signature);
			}
			if (algos.encryption) {
				parts.push(_('Cipher') + ': ' + algos.encryption.toUpperCase());
			}
			if (algos.key_transport) {
				parts.push(_('Key transport') + ': ' + algos.key_transport);
			}
			return parts.join('<br>');
		},

		/**
		 * Format a short algorithm tag for the info bar (compact, single-line).
		 *
		 * @param {Object} algos algorithm details from the backend (may be undefined)
		 * @return {String} formatted HTML string like " [SHA-256, AES-256-GCM]", or empty
		 */
		formatAlgorithmTag: function (algos) {
			if (!algos) {
				return '';
			}
			var parts = [];
			if (algos.digest) {
				parts.push(algos.digest.toUpperCase());
			}
			if (algos.encryption) {
				parts.push(algos.encryption.toUpperCase());
			}
			if (parts.length === 0) {
				return '';
			}
			return ' <span class="smime-algo-tag">[' + parts.join(', ') + ']</span>';
		}
	};
}();
