/*
  Copyright (C) 2013-2020 Grégory Soutadé

  This file is part of gPass.

  gPass is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  gPass 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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with gPass.  If not, see <http://www.gnu.org/licenses/>.
*/

var gpass_enabled = true;

function _notification(message, data)
{
    if (message !== data)
	message += data;

    options = {
	type: "basic",
	title : "gPass",
	message : message,
	iconUrl:browser.extension.getURL("icons/gpass_icon_64.png")
    };

    browser.notifications.create(options).then(
	function created(notification_id)
	{
	    window.setTimeout(function() {
		browser.notifications.clear(notification_id);
	    }, 2000);
	}
    );
}

function _add_name(logins, name)
{
    for(var i=0; i<logins.length; i++)
	if (logins[i] == name) return ;
    logins.push(name);
}

function try_get_name(fields, type_filters, match)
{
    var user = null;
    var all_logins = new Array();

    for (var i=0; i<fields.length; i++)
    {
	var field = fields[i];

	for (var a=0; a<type_filters.length; a++)
	{
	    if ((match && field.getAttribute("type") == type_filters[a]) ||
		(!match && field.getAttribute("type") != type_filters[a]))
	    {
		if (field.hasAttribute("name") && field.value != "")
		{
		    name = field.getAttribute("name");
		    // Subset of common user field
		    if      (name == "user")     user = field.value;
		    else if (name == "usr")      user = field.value;
		    else if (name == "username") user = field.value;
		    else if (name == "login")    user = field.value;

		    _add_name(all_logins, field.value);
		}
	    }
	}
    }

    if (user != null)
	return new Array(user);
    else
	return all_logins;
}

function on_focus(e)
{
    if (gpass_enabled)
    {
	parameters = {
	    type:"update_icon",
	    icon_id:GPASS_ICON.ACTIVATED,
	};
	browser.runtime.sendMessage(parameters, {});
    }
}

function on_blur(e)
{
    if (gpass_enabled)
    {
	parameters = {
	    type:"update_icon",
	    icon_id:GPASS_ICON.NORMAL,
	};
	browser.runtime.sendMessage(parameters, {});
    }
}

function on_sumbit(e)
{
    var form = this;
    var fields = form.getElementsByTagName("input");
    var domain = form.ownerDocument.baseURI;
    var password_computed = false;

    debug("on_submit");

    type_filters = new Array();
    // Get all <input type="text"> && <input type="email">
    type_filters.push("text");
    type_filters.push("email");
    logins = try_get_name(fields, type_filters, true);

    // Get all other fields except text, email and password
    type_filters.push("password");
    all_logins = try_get_name(fields, type_filters, false);

    if (!logins.length)
	logins = all_logins;

    e.preventDefault();

    // Look for <input type="password" value="@@...">
    for (var i=0; i<fields.length; i++)
    {
	var field = fields[i];

	if (field.getAttribute("type") == "password")
	{
	    password = field.value;
	    if (!password.startsWith("@@") && !password.startsWith("@_"))
		continue;

	    // Remove current value to limit master key stealing
	    field.value = "";
	    password_computed = true;
	    do_submit = !password.startsWith("@_");
	    mkey = password.substring(2);

	    parameters = {
		type:"password",
		logins:logins,
		domain:domain,
		mkey:mkey,
		options:{field_id:i}
	    };

	    browser.runtime.sendMessage(parameters, {},
				       function (response) {
					   debug(response);
					   var field = fields[response.options.field_id];
	    				   switch(response.value)
	    				   {
	    				       case SERVER.OK:
					       set_password(form, field, response.password, do_submit)
					       notify("Password successfully replaced", "");
	    				       break;
	    				       case SERVER.FAILED:
	    				       if (logins.length != all_logins.length)
	    				       {
	    				       	   parameters[logins] = all_logins;
	    				       	   browser.runtime.sendMessage(parameters);
	    				       }
	    				       break;
	    				       case SERVER.RESTART_REQUEST:
	    				       i = -1; // Restart loop
	    				       break;
	    				   }
				       }
				      );
	}
    }

    if (!password_computed)
    {
	debug("No password computed");
	form.submit();
	return true;
    }

    return false;
}

function set_password(form, field, password, do_submit)
{
    field.value = password;
    // Remove gPass event listener and submit again with clear password
    unblock_all_forms();
    if (do_submit)
    {
	// Propagate change
	change_cb = field.onchange;
	if (change_cb)
	    change_cb();
	// Try to type "enter"
	var evt = new KeyboardEvent("keydown");
	delete evt.which;
	evt.which = 13;
	field.dispatchEvent(evt);
	// Submit form
	form.submit();
    }
}

var managed_forms = new Array();

function block_all_forms(doc, do_block)
{
    var first_time = (managed_forms.length == 0);
    var cur_focused_element = document.activeElement;
    
    debug("block all forms");

    gpass_enabled = do_block;

    // If there is a password in the form, add a "submit" listener
    for(var i=0; i<doc.forms.length; i++)
    {
	var form = doc.forms[i];
	var fields = form.getElementsByTagName("input");
	for (var a=0; a<fields.length; a++)
	{
	    var field = fields[a];
	    if (field.getAttribute("type") == "password")
	    {
		if (do_block)
		{
		    block_url(form.action);
		    old_cb = form.onsubmit;
		    if (old_cb)
			form.removeEventListener("submit", old_cb);
		    form.addEventListener("submit", on_sumbit);
		    if (old_cb)
			form.addEventListener("submit", old_cb);
		    field.addEventListener("focus", on_focus);
		    field.addEventListener("blur", on_blur);
		    if (cur_focused_element === field)
			on_focus(null);
		}
		if (first_time)
		    managed_forms.push(form);
		break;
	    }
	}
    }

    /* Request can be sent to another URL... */
    if (managed_forms.length && do_block)
    	block_url("<all_urls>");
}

function unblock_all_forms()
{
    debug("unblock all forms");

    on_blur(null);
    
    for(var i=0; i<managed_forms.length; i++)
    {
	var form = managed_forms[i];

	form.removeEventListener("submit", on_sumbit);
	unblock_url(form.action);

	var fields = form.getElementsByTagName("input");
	for (var a=0; a<fields.length; a++)
	{
	    var field = fields[a];
	    if (field.getAttribute("type") == "password")
	    {
		field.removeEventListener("focus", on_focus);
		field.removeEventListener("blur", on_blur);
		break;
	    }
	}
    }

    if (managed_forms.length)
	unblock_url("<all_urls>");

    gpass_enabled = false;
}

browser.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {

	if (request.type == "getUsername")
	{
	    debug("getUsername");
	    if (managed_forms.length == 1)
	    {
		fields = managed_forms[0].getElementsByTagName("input");

		type_filters = new Array();
		// Get all <input type="text"> && <input type="email">
		type_filters.push("text");
		type_filters.push("email");
		logins = try_get_name(fields, type_filters, true);

		if (logins.length == 1)
		    sendResponse(logins[0]);
		else
		    sendResponse("");
	    }
	    else
		sendResponse("");
	}
	else if (request.type == "setPassword")
	{
	    debug("setPassword");
	    var response = "";
	    if (managed_forms.length == 1)
	    {
		fields = managed_forms[0].getElementsByTagName("input");
		password_field = null;
		
		for (a=0; a<fields.length; a++)
		{
		    field = fields[a];
		    if (field.getAttribute("type") == "password")
		    {
			if (password_field == null)
			    password_field = field;
			else
			{
			    // More than one password field : abort
			    password_field = null;
			    break;
			}
		    }
		}

		if (password_field)
		{
		    set_password(managed_forms[0], password_field,
				 request.password, request.submit);
		    response = "ok";
		}
	    }
	    sendResponse(response);
	    return true;
	}
	else if (request.type == "blockForms")
	{
	    block_all_forms(document, true);
	}
	else if (request.type == "unblockForms")
	{
	    unblock_all_forms();
	}
    });

function document_loaded()
{
    parameters = {
    	"type": "is_gpass_enabled",
    };

    browser.runtime.sendMessage(parameters, {},
    				function (response) {
				    if (response)
    					block_all_forms(document, response.enabled);
    				});
}

document_loaded();

console.log("Welcome to gPass web extension v0.9 !");
console.log("Privacy Policy can be found at http://indefero.soutade.fr/p/gpass/source/tree/master/PrivacyPolicy.md");
console.log("");