/* 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("");