Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f8c250897 | |||
| 5784e61e40 | |||
| 63f88a2076 | |||
|
|
b8223a87ad | ||
| 129335dc2d | |||
|
|
2cb26d6d40 | ||
|
|
54565f90b6 | ||
|
|
42142cbca9 | ||
|
|
2def010612 | ||
|
|
daa7c3b44c |
25
ChangeLog
25
ChangeLog
@@ -1,3 +1,28 @@
|
||||
**v1.3 :**
|
||||
|
||||
Server
|
||||
* Improve filter URL match
|
||||
|
||||
Addon
|
||||
* Minor bug fixes
|
||||
|
||||
CLI
|
||||
|
||||
**v1.2 :**
|
||||
|
||||
Server
|
||||
* Rework UI
|
||||
* Display PHP parsed URL in new URL case, not raw URL
|
||||
* Add a filter for unciphered passwords (supports regular expressions)
|
||||
* Add a button to copy unciphered password into clipboard
|
||||
* Don't clear URL and login when adding a new password
|
||||
* Change alert() by button name update for "Update" and "Copy clipboard" functions
|
||||
|
||||
Addon
|
||||
* Update manifest to v3 (Chrome only)
|
||||
|
||||
CLI
|
||||
|
||||
**v1.1 :**
|
||||
|
||||
Server
|
||||
|
||||
14
README.md
14
README.md
@@ -81,19 +81,7 @@ The dependencies are libcurl and OpenSSL (-dev packages : ie _libcurl4-openssl-d
|
||||
A sample configuration file is available _gpass.ini.sample_
|
||||
|
||||
|
||||
Version Information
|
||||
-------------------
|
||||
|
||||
Current version is 1.1.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
All the code is licensed under GPL v3. Source code is available [here](http://indefero.soutade.fr/p/gpass).
|
||||
|
||||
|
||||
Mailing list
|
||||
------------
|
||||
|
||||
You can subscribe to the announce list @ https://pannous.soutade.fr/lists/gpass
|
||||
All the code is licensed under GPL v3. Source code is available [here](https://forge.soutade.fr/soutade/gPass).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2013-2020 Grégory Soutadé
|
||||
Copyright (C) 2013-2022 Grégory Soutadé
|
||||
|
||||
This file is part of gPass.
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
along with gPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {parseUri} from "./lib/parseuri.js";
|
||||
import {SERVER, GPASS_ICON, wildcard_domain, simple_pbkdf2, crypto_pbkdf2,
|
||||
encrypt_ecb, encrypt_cbc, decrypt_cbc, digest, a2hex, hex2a, debug} from "./lib/misc.js";
|
||||
import {get_preference, set_preference, delete_preference} from "./compat.js";
|
||||
|
||||
var browser = browser || chrome;
|
||||
var protocol_version = 4;
|
||||
var account_url = null;
|
||||
@@ -26,16 +31,14 @@ function _notification(message, data)
|
||||
if (message !== data)
|
||||
message += data;
|
||||
|
||||
options = {
|
||||
var options = {
|
||||
type: "basic",
|
||||
title : "gPass",
|
||||
message : message,
|
||||
iconUrl:browser.extension.getURL("icons/gpass_icon_64.png")
|
||||
iconUrl:"icons/gpass_icon_64.png"
|
||||
};
|
||||
|
||||
browser.notifications.create("gPass", options, function(){});
|
||||
|
||||
window.setTimeout(function() {browser.notifications.clear("gPass", function(){})}, 2000);
|
||||
}
|
||||
|
||||
async function generate_request(domain, login, mkey, iv)
|
||||
@@ -44,34 +47,36 @@ async function generate_request(domain, login, mkey, iv)
|
||||
debug("will encrypt " + v);
|
||||
while ((v.length % 16))
|
||||
v += "\0";
|
||||
hash = await digest(v);
|
||||
var hash = await digest(crypto, v);
|
||||
v += hash.slice(8, 24);
|
||||
enc = encrypt_cbc(mkey, iv, v);
|
||||
var enc = encrypt_cbc(mkey, iv, v);
|
||||
|
||||
return enc;
|
||||
}
|
||||
|
||||
async function ask_server(logins, domain, wdomain, mkey, sendResponse, options)
|
||||
{
|
||||
account_url = await get_preference("account_url");
|
||||
var account_url = await get_preference("account_url");
|
||||
|
||||
var salt = parseURI.parseUri(account_url);
|
||||
var salt = parseUri(account_url);
|
||||
salt = salt["host"] + salt["path"];
|
||||
|
||||
debug("salt " + salt);
|
||||
|
||||
pbkdf2_level = await get_preference("pbkdf2_level");
|
||||
var pbkdf2_level = await get_preference("pbkdf2_level");
|
||||
|
||||
global_iv = await simple_pbkdf2(salt, mkey, pbkdf2_level);
|
||||
var global_iv = await simple_pbkdf2(crypto, salt, mkey, pbkdf2_level);
|
||||
global_iv = global_iv.slice(0, 16);
|
||||
mkey = crypto_pbkdf2(mkey, salt, pbkdf2_level);
|
||||
var mkey = crypto_pbkdf2(crypto, mkey, salt, pbkdf2_level);
|
||||
|
||||
debug("global_iv " + a2hex(global_iv));
|
||||
|
||||
keys = "";
|
||||
var keys = "";
|
||||
var key_index;
|
||||
var a;
|
||||
for(key_index=0, a=0; a<logins.length; a++, key_index++)
|
||||
{
|
||||
enc = await generate_request(domain, logins[a], mkey, global_iv);
|
||||
var enc = await generate_request(domain, logins[a], mkey, global_iv);
|
||||
keys += (keys.length != 0) ? "&" : "";
|
||||
keys += "k" + key_index + "=" + a2hex(enc);
|
||||
|
||||
@@ -85,143 +90,147 @@ async function ask_server(logins, domain, wdomain, mkey, sendResponse, options)
|
||||
|
||||
debug("Keys " + keys);
|
||||
|
||||
var gPassRequest = new XMLHttpRequest();
|
||||
|
||||
var ret = SERVER.OK;
|
||||
|
||||
// gPassRequest.addEventListener("progress", function(evt) { ; }, false);
|
||||
gPassRequest.addEventListener("load", async function(evt) {
|
||||
var ciphered_password = "";
|
||||
var clear_password = "";
|
||||
var server_pbkdf2_level = 0;
|
||||
var server_version = 0;
|
||||
var matched_key = 0;
|
||||
debug("connect to " + account_url);
|
||||
const headers = {'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'};
|
||||
const request = new Request(account_url, {method: 'POST', headers: headers, body: keys});
|
||||
|
||||
const response = await fetch(request).catch((e) => {
|
||||
debug(e);
|
||||
_notification("Network error : check your server address", "");
|
||||
});
|
||||
|
||||
var r = this.responseText.split("\n");
|
||||
debug("resp " + r);
|
||||
if (response === undefined)
|
||||
{
|
||||
ret = SERVER.ERROR;
|
||||
sendResponse({"value": ret, options:options});
|
||||
return;
|
||||
}
|
||||
|
||||
const responseText = await response.text();
|
||||
|
||||
for(var a=0; a<r.length; a++)
|
||||
var ciphered_password = "";
|
||||
var server_pbkdf2_level = 0;
|
||||
var server_version = 0;
|
||||
var matched_key = 0;
|
||||
|
||||
var r = responseText.split("\n");
|
||||
debug("resp " + r);
|
||||
|
||||
for(var a=0; a<r.length; a++)
|
||||
{
|
||||
debug("Analyse " + r[a]);
|
||||
|
||||
var params = r[a].split("=");
|
||||
if (params.length != 2 && params[0] != "<end>")
|
||||
{
|
||||
debug("Analyse " + r[a]);
|
||||
_notification("Error : It seems that it's not a gPass server",
|
||||
responseText);
|
||||
ret = SERVER.FAILED;
|
||||
break;
|
||||
}
|
||||
|
||||
params = r[a].split("=");
|
||||
if (params.length != 2 && params[0] != "<end>")
|
||||
switch(params[0])
|
||||
{
|
||||
case "protocol":
|
||||
debug("protocol : " + params[1]);
|
||||
|
||||
if (params[1].indexOf("gpass-") != 0)
|
||||
{
|
||||
_notification("Error : It seems that it's not a gPass server",
|
||||
this.responseText);
|
||||
responseText);
|
||||
ret = SERVER.FAILED;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(params[0])
|
||||
{
|
||||
case "protocol":
|
||||
debug("protocol : " + params[1]);
|
||||
var server_protocol_version = params[1].match(/\d+/)[0];
|
||||
|
||||
if (params[1].indexOf("gpass-") != 0)
|
||||
if (server_protocol_version > protocol_version)
|
||||
{
|
||||
_notification("Protocol version not supported, please upgrade your addon", "");
|
||||
ret = SERVER.FAILED;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (server_protocol_version)
|
||||
{
|
||||
_notification("Error : It seems that it's not a gPass server",
|
||||
this.responseText);
|
||||
ret = SERVER.FAILED;
|
||||
case 2:
|
||||
server_pbkdf2_level = 1000;
|
||||
break;
|
||||
case 3:
|
||||
// Version 3 : nothing special to do
|
||||
case 4:
|
||||
// Version 4 : nothing special to do
|
||||
break;
|
||||
}
|
||||
|
||||
server_protocol_version = params[1].match(/\d+/)[0];
|
||||
|
||||
if (server_protocol_version > protocol_version)
|
||||
{
|
||||
_notification("Protocol version not supported, please upgrade your addon", "");
|
||||
ret = SERVER.FAILED;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (server_protocol_version)
|
||||
{
|
||||
case 2:
|
||||
server_pbkdf2_level = 1000;
|
||||
break;
|
||||
case 3:
|
||||
// Version 3 : nothing special to do
|
||||
case 4:
|
||||
// Version 4 : nothing special to do
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "matched_key":
|
||||
matched_key = params[1];
|
||||
case "pass":
|
||||
ciphered_password = params[1];
|
||||
break;
|
||||
case "pkdbf2_level":
|
||||
case "pbkdf2_level":
|
||||
server_pbkdf2_level = parseInt(params[1].match(/\d+/)[0], 10);
|
||||
if (server_pbkdf2_level != NaN &&
|
||||
server_pbkdf2_level != pbkdf2_level &&
|
||||
server_pbkdf2_level >= 1000) // Minimum level for PBKDF2 !
|
||||
{
|
||||
debug("New pbkdf2 level " + server_pbkdf2_level);
|
||||
pbkdf2_level = server_pbkdf2_level;
|
||||
set_preference("pbkdf2_level", pbkdf2_level, null);
|
||||
ret = SERVER.RESTART_REQUEST;
|
||||
}
|
||||
break;
|
||||
case "<end>":
|
||||
break;
|
||||
default:
|
||||
debug("Unknown command " + params[0]);
|
||||
|
||||
_notification("Error : It seems that it's not a gPass server",
|
||||
this.responseText);
|
||||
ret = SERVER.FAILED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != SERVER.OK)
|
||||
{
|
||||
sendResponse({"value": ret, options:options});
|
||||
return;
|
||||
}
|
||||
|
||||
if (ciphered_password != "")
|
||||
{
|
||||
debug("Ciphered password : " + ciphered_password);
|
||||
clear_password = await decrypt_cbc(mkey, global_iv, hex2a(ciphered_password));
|
||||
clear_password = clear_password.replace(/\0*$/, "");
|
||||
clear_password = clear_password.substr(3, clear_password.length);
|
||||
debug("Clear password " + clear_password);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("No password found");
|
||||
break;
|
||||
case "matched_key":
|
||||
matched_key = params[1];
|
||||
case "pass":
|
||||
ciphered_password = params[1];
|
||||
break;
|
||||
case "pkdbf2_level":
|
||||
case "pbkdf2_level":
|
||||
server_pbkdf2_level = parseInt(params[1].match(/\d+/)[0], 10);
|
||||
if (server_pbkdf2_level != NaN &&
|
||||
server_pbkdf2_level != pbkdf2_level &&
|
||||
server_pbkdf2_level >= 1000) // Minimum level for PBKDF2 !
|
||||
{
|
||||
debug("New pbkdf2 level " + server_pbkdf2_level);
|
||||
pbkdf2_level = server_pbkdf2_level;
|
||||
set_preference("pbkdf2_level", pbkdf2_level, null);
|
||||
ret = SERVER.RESTART_REQUEST;
|
||||
}
|
||||
break;
|
||||
case "<end>":
|
||||
break;
|
||||
default:
|
||||
debug("Unknown command " + params[0]);
|
||||
|
||||
_notification("Error : It seems that it's not a gPass server",
|
||||
responseText);
|
||||
ret = SERVER.FAILED;
|
||||
|
||||
_notification("No password found in database", "")
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != SERVER.OK)
|
||||
{
|
||||
sendResponse({"value": ret, options:options});
|
||||
return;
|
||||
}
|
||||
|
||||
if (ciphered_password != "")
|
||||
{
|
||||
debug("Ciphered password : " + ciphered_password);
|
||||
var clear_password = await decrypt_cbc(mkey, global_iv, hex2a(ciphered_password));
|
||||
clear_password = clear_password.replace(/\0*$/, "");
|
||||
clear_password = clear_password.substr(3, clear_password.length);
|
||||
debug("Clear password " + clear_password);
|
||||
sendResponse({"value": ret, "password":clear_password, "options":options});
|
||||
}, false);
|
||||
gPassRequest.addEventListener("error", function(evt) {
|
||||
debug("error");
|
||||
ret = false;
|
||||
_notification("Error");
|
||||
}, false);
|
||||
debug("connect to " + account_url);
|
||||
gPassRequest.open("POST", account_url, true);
|
||||
gPassRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
|
||||
gPassRequest.send(keys);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("No password found");
|
||||
|
||||
return true;
|
||||
ret = SERVER.FAILED;
|
||||
|
||||
_notification("No password found in database", "")
|
||||
|
||||
sendResponse({"value": ret, "options":options});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function update_gpass_icon(iconId, tabId)
|
||||
{
|
||||
debug("update_gpass_icon");
|
||||
|
||||
icon_infos = {"tabId":tabId};
|
||||
icon_name = "";
|
||||
var icon_infos = {"tabId":tabId};
|
||||
var icon_name = "";
|
||||
|
||||
switch (iconId)
|
||||
{
|
||||
@@ -237,6 +246,8 @@ function update_gpass_icon(iconId, tabId)
|
||||
|
||||
debug(icon_name);
|
||||
|
||||
var icon_infos = {};
|
||||
|
||||
icon_infos["path"] = {
|
||||
16:"icons/gpass" + icon_name + "_icon_16.png",
|
||||
32:"icons/gpass" + icon_name + "_icon_32.png",
|
||||
@@ -244,7 +255,10 @@ function update_gpass_icon(iconId, tabId)
|
||||
128:"icons/gpass" + icon_name + "_icon_128.png",
|
||||
};
|
||||
|
||||
browser.browserAction.setIcon(icon_infos);
|
||||
if (browser.browserAction)
|
||||
browser.browserAction.setIcon(icon_infos);
|
||||
else
|
||||
browser.action.setIcon(icon_infos);
|
||||
}
|
||||
|
||||
async function is_gpass_enabled(uri)
|
||||
@@ -262,7 +276,7 @@ async function is_gpass_enabled(uri)
|
||||
else
|
||||
{
|
||||
debug("Check for enable");
|
||||
var domain = parseURI.parseUri(uri);
|
||||
var domain = parseUri(uri);
|
||||
domain = domain["host"];
|
||||
debug("Is gpass enabled for " + domain + " ?");
|
||||
|
||||
@@ -274,10 +288,10 @@ async function is_gpass_enabled(uri)
|
||||
|
||||
function save_gpass_enable_config(uri, enable)
|
||||
{
|
||||
var domain = parseURI.parseUri(uri);
|
||||
var domain = parseUri(uri);
|
||||
domain = domain["host"];
|
||||
|
||||
key = "disable-" + domain;
|
||||
var key = "disable-" + domain;
|
||||
if (enable)
|
||||
{
|
||||
debug("Enable gpass for " + domain);
|
||||
@@ -296,7 +310,7 @@ function _query_tabs_is_gpass_enabled(tabs, sendResponse)
|
||||
{
|
||||
is_gpass_enabled(tabs[0].url).then(
|
||||
function (key_present) {
|
||||
enabled = (key_present == null);
|
||||
var enabled = (key_present == null);
|
||||
update_gpass_icon((enabled)?GPASS_ICON.NORMAL:GPASS_ICON.DISABLED, tabs[0].id);
|
||||
sendResponse({"enabled":enabled});
|
||||
}
|
||||
@@ -321,6 +335,8 @@ function _query_tabs_update_icon(tabs, iconId)
|
||||
|
||||
function update_enable(enabled, tab, saveConfig)
|
||||
{
|
||||
var parameters;
|
||||
|
||||
if (enabled)
|
||||
{
|
||||
parameters = {type:"blockForms"};
|
||||
@@ -345,20 +361,54 @@ function gpass_switch_enable(tab)
|
||||
is_gpass_enabled(tab.url).then(
|
||||
function (key_present)
|
||||
{
|
||||
enabled = (key_present == null);
|
||||
var enabled = (key_present == null);
|
||||
// Do switch
|
||||
enabled = !enabled;
|
||||
update_enable(enabled, tab, true);
|
||||
});
|
||||
}
|
||||
|
||||
function createMenus(browser)
|
||||
{
|
||||
var title;
|
||||
|
||||
debug("Create menus");
|
||||
|
||||
/* Not supported by Chrome */
|
||||
if (browser.menus.onShown)
|
||||
title = 'Disable form\'s hook for this website';
|
||||
else
|
||||
title = 'Disable or enable form\'s hook for this website';
|
||||
|
||||
/* Enable/disable */
|
||||
browser.menus.create({
|
||||
id: 'switch_enable',
|
||||
title: title,
|
||||
contexts: ['action'],
|
||||
}, () => {console.log(chrome.runtime.lastError);});
|
||||
|
||||
/* Not supported by Chrome */
|
||||
if (browser.menus.onShown)
|
||||
title = 'Disable form\'s hook for ALL websites';
|
||||
else
|
||||
title = 'Disable or enable form\'s hook for ALL websites';
|
||||
|
||||
/* Always enable/disable */
|
||||
browser.menus.create({
|
||||
id: 'always_disable',
|
||||
title: title,
|
||||
contexts: ['action']
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function extension_load()
|
||||
{
|
||||
browser.runtime.onMessage.addListener(
|
||||
function(request, sender, sendResponse) {
|
||||
if (request.type == "password")
|
||||
{
|
||||
var domain = parseURI.parseUri(request.domain);
|
||||
var domain = parseUri(request.domain);
|
||||
domain = domain["host"];
|
||||
var wdomain = wildcard_domain(domain);
|
||||
|
||||
@@ -390,19 +440,19 @@ function extension_load()
|
||||
}
|
||||
else if (request.type == "is_gpass_enabled")
|
||||
{
|
||||
browser.tabs.query({active:true, currentWindow:true},
|
||||
function cb(tabs) {
|
||||
_query_tabs_is_gpass_enabled(tabs, sendResponse);
|
||||
});
|
||||
browser.tabs.query({active:true, currentWindow:true}).then( (tabs) =>
|
||||
{
|
||||
_query_tabs_is_gpass_enabled(tabs, sendResponse);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
else if (request.type == "update_icon")
|
||||
{
|
||||
debug("update_icon");
|
||||
browser.tabs.query({active:true, currentWindow:true},
|
||||
function cb(tabs) {
|
||||
_query_tabs_update_icon(tabs, request.icon_id);
|
||||
});
|
||||
browser.tabs.query({active:true, currentWindow:true}).then( (tabs) =>
|
||||
{
|
||||
_query_tabs_update_icon(tabs, request.icon_id);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -411,116 +461,88 @@ function extension_load()
|
||||
}
|
||||
);
|
||||
|
||||
/* Chrome */
|
||||
if (!browser.menus && browser.contextMenus)
|
||||
{
|
||||
browser.menus = browser.contextMenus;
|
||||
}
|
||||
|
||||
/* Settings */
|
||||
browser.menus.create({
|
||||
id: 'settings',
|
||||
title: 'gPass Settings',
|
||||
contexts: ['browser_action']
|
||||
});
|
||||
|
||||
/* Not supported by Chrome */
|
||||
if (browser.menus.onShown)
|
||||
title = 'Disable gPass for this website';
|
||||
else
|
||||
title = 'Disable or enable gPass for this website';
|
||||
|
||||
/* Enable/disable */
|
||||
browser.menus.create({
|
||||
id: 'switch_enable',
|
||||
title: title,
|
||||
contexts: ['browser_action']
|
||||
});
|
||||
|
||||
/* Not supported by Chrome */
|
||||
if (browser.menus.onShown)
|
||||
title = 'Disable gPass for ALL websites';
|
||||
else
|
||||
title = 'Disable or enable gPass for ALL websites';
|
||||
|
||||
/* Always enable/disable */
|
||||
browser.menus.create({
|
||||
id: 'always_disable',
|
||||
title: title,
|
||||
contexts: ['browser_action']
|
||||
browser.runtime.onInstalled.addListener(() => {
|
||||
createMenus(browser)
|
||||
});
|
||||
|
||||
browser.menus.onClicked.addListener(
|
||||
function(info, tab) {
|
||||
switch (info.menuItemId) {
|
||||
case 'settings':
|
||||
browser.runtime.openOptionsPage();
|
||||
break;
|
||||
function(info, tab) {
|
||||
switch (info.menuItemId) {
|
||||
|
||||
case 'always_disable':
|
||||
get_preference('always_disabled').then(
|
||||
function (always_disabled) {
|
||||
case 'always_disable':
|
||||
get_preference('always_disabled').then(
|
||||
function (always_disabled) {
|
||||
debug('Change always disable');
|
||||
debug(always_disabled);
|
||||
always_disabled = !always_disabled;
|
||||
always_disabled = !always_disabled;
|
||||
|
||||
set_preference('always_disabled', always_disabled,
|
||||
function(error)
|
||||
{
|
||||
browser.tabs.query({},
|
||||
function cb(tabs) {
|
||||
for (i=0; i<tabs.length; i++)
|
||||
browser.tabs.query({active:true, currentWindow:true},
|
||||
(tabs) => {
|
||||
for (var i=0; i<tabs.length; i++)
|
||||
update_enable(!always_disabled, tabs[i], false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 'switch_enable':
|
||||
gpass_switch_enable(tab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 'switch_enable':
|
||||
gpass_switch_enable(tab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
/* Firefox only */
|
||||
if (browser.menus.onShown)
|
||||
{
|
||||
browser.menus.onShown.addListener(
|
||||
function(info, tab) {
|
||||
is_gpass_enabled(tab.url).then(
|
||||
function (key_present) {
|
||||
enabled = (key_present == null);
|
||||
if (enabled)
|
||||
title = 'Disable gPass for this website';
|
||||
else
|
||||
title = 'Enable gPass for this website';
|
||||
browser.menus.update("switch_enable",
|
||||
{
|
||||
"title":title
|
||||
}
|
||||
);
|
||||
browser.menus.refresh();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
browser.menus.onShown.addListener(
|
||||
function(info, tab) {
|
||||
is_gpass_enabled(tab.url).then(
|
||||
function (key_present) {
|
||||
enabled = (key_present == null);
|
||||
if (enabled)
|
||||
title = 'Disable gPass for this website';
|
||||
else
|
||||
title = 'Enable gPass for this website';
|
||||
browser.menus.update("switch_enable",
|
||||
{
|
||||
"title":title
|
||||
}
|
||||
);
|
||||
browser.menus.refresh();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
browser.menus.onShown.addListener(
|
||||
function(info, tab) {
|
||||
get_preference('always_disabled').then(
|
||||
function (always_disabled) {
|
||||
if (always_disabled)
|
||||
title = 'Enable gPass for ALL websites';
|
||||
else
|
||||
title = 'Disable gPass for ALL websites';
|
||||
browser.menus.update("always_disable",
|
||||
{
|
||||
"title":title
|
||||
}
|
||||
);
|
||||
browser.menus.refresh();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
browser.menus.onShown.addListener(
|
||||
function(info, tab) {
|
||||
get_preference('always_disabled').then(
|
||||
function (always_disabled) {
|
||||
if (always_disabled)
|
||||
title = 'Enable gPass for ALL websites';
|
||||
else
|
||||
title = 'Disable gPass for ALL websites';
|
||||
browser.menus.update("always_disable",
|
||||
{
|
||||
"title":title
|
||||
}
|
||||
);
|
||||
browser.menus.refresh();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,5 +562,4 @@ async function self_test()
|
||||
}
|
||||
|
||||
//self_test();
|
||||
|
||||
extension_load();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2013-2020 Grégory Soutadé
|
||||
Copyright (C) 2013-2022 Grégory Soutadé
|
||||
|
||||
This file is part of gPass.
|
||||
|
||||
@@ -23,7 +23,7 @@ var default_preferences = {"pbkdf2_level": 1000,
|
||||
"popup_clipboard":false
|
||||
};
|
||||
|
||||
function get_preference(key)
|
||||
export function get_preference(key)
|
||||
{
|
||||
// Inspired from https://github.com/akiomik/chrome-storage-promise/
|
||||
var promise = new Promise((resolve, reject) => {
|
||||
@@ -50,9 +50,9 @@ function get_preference(key)
|
||||
return promise;
|
||||
}
|
||||
|
||||
function set_preference(key, value, sendResponse)
|
||||
export function set_preference(key, value, sendResponse)
|
||||
{
|
||||
pref = {[key]:value};
|
||||
var pref = {[key]:value};
|
||||
chrome.storage.local.set(pref, function (result) {
|
||||
if (chrome.runtime.lastError)
|
||||
alert(chrome.runtime.lastError);
|
||||
@@ -61,12 +61,13 @@ function set_preference(key, value, sendResponse)
|
||||
});
|
||||
}
|
||||
|
||||
function delete_preference(key)
|
||||
export function delete_preference(key)
|
||||
{
|
||||
chrome.storage.local.remove(key);
|
||||
}
|
||||
|
||||
function send_tab_message(tab_id, parameters, callback)
|
||||
export function send_tab_message(tab_id, parameters, callback)
|
||||
{
|
||||
chrome.tabs.sendMessage(tab_id, parameters, {}, callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2013-2020 Grégory Soutadé
|
||||
Copyright (C) 2013-2022 Grégory Soutadé
|
||||
|
||||
This file is part of gPass.
|
||||
|
||||
@@ -17,30 +17,27 @@
|
||||
along with gPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var gpass_enabled = true;
|
||||
/* from misc.js */
|
||||
/* Can't directly add it, because it's now a module */
|
||||
var DEBUG = false;
|
||||
var browser = browser || chrome;
|
||||
const SERVER = {OK : 0, FAILED : 1, RESTART_REQUEST : 2};
|
||||
const GPASS_ICON = {NORMAL:0, DISABLED:1, ACTIVATED:2};
|
||||
|
||||
function _notification(message, data)
|
||||
function debug(s)
|
||||
{
|
||||
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);
|
||||
}
|
||||
);
|
||||
if (DEBUG)
|
||||
console.log(s);
|
||||
}
|
||||
|
||||
function notify(text, data)
|
||||
{
|
||||
browser.runtime.sendMessage({type: "notification", options:{"message":text, "data":data}});
|
||||
}
|
||||
|
||||
|
||||
var gpass_enabled = true;
|
||||
|
||||
function _add_name(logins, name)
|
||||
{
|
||||
for(var i=0; i<logins.length; i++)
|
||||
@@ -77,7 +74,7 @@ function try_get_name(fields, type_filters, match)
|
||||
{
|
||||
if (field.hasAttribute("name") && field.value != "")
|
||||
{
|
||||
name = field.getAttribute("name");
|
||||
var name = field.getAttribute("name");
|
||||
// Subset of common user field
|
||||
if (name == "user") user = field.value;
|
||||
else if (name == "usr") user = field.value;
|
||||
@@ -130,7 +127,7 @@ function on_focus(e)
|
||||
|
||||
if (logins.length || all_logins.length)
|
||||
{
|
||||
parameters = {
|
||||
var parameters = {
|
||||
type:"update_icon",
|
||||
icon_id:GPASS_ICON.ACTIVATED,
|
||||
};
|
||||
@@ -143,7 +140,7 @@ function on_blur(e)
|
||||
if (!gpass_enabled)
|
||||
return;
|
||||
|
||||
parameters = {
|
||||
var parameters = {
|
||||
type:"update_icon",
|
||||
icon_id:GPASS_ICON.NORMAL,
|
||||
};
|
||||
@@ -159,8 +156,8 @@ function on_sumbit(e)
|
||||
|
||||
debug("on_submit");
|
||||
|
||||
logins = get_logins(form, true);
|
||||
all_logins = get_logins(form, false);
|
||||
var logins = get_logins(form, true);
|
||||
var all_logins = get_logins(form, false);
|
||||
|
||||
if (!logins.length)
|
||||
logins = all_logins;
|
||||
@@ -174,17 +171,17 @@ function on_sumbit(e)
|
||||
|
||||
if (field.getAttribute("type") == "password")
|
||||
{
|
||||
password = field.value;
|
||||
var 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);
|
||||
var do_submit = !password.startsWith("@_");
|
||||
var mkey = password.substring(2);
|
||||
|
||||
parameters = {
|
||||
var parameters = {
|
||||
type:"password",
|
||||
logins:logins,
|
||||
domain:domain,
|
||||
@@ -195,18 +192,19 @@ function on_sumbit(e)
|
||||
browser.runtime.sendMessage(parameters, {},
|
||||
function (response) {
|
||||
debug(response);
|
||||
var field = fields[response.options.field_id];
|
||||
switch(response.value)
|
||||
{
|
||||
case SERVER.OK:
|
||||
var field = fields[response.options.field_id];
|
||||
set_password(form, field, response.password, do_submit)
|
||||
notify("Password successfully replaced", "");
|
||||
break;
|
||||
case SERVER.FAILED:
|
||||
if (logins.length != all_logins.length)
|
||||
if (logins.length != all_logins.length && all_logins.length != 0)
|
||||
{
|
||||
parameters[logins] = all_logins;
|
||||
browser.runtime.sendMessage(parameters);
|
||||
debug("Try with all logins");
|
||||
parameters[logins] = all_logins;
|
||||
browser.runtime.sendMessage(parameters);
|
||||
}
|
||||
break;
|
||||
case SERVER.RESTART_REQUEST:
|
||||
@@ -214,18 +212,18 @@ function on_sumbit(e)
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!password_computed)
|
||||
{
|
||||
debug("No password computed");
|
||||
form.submit();
|
||||
return true;
|
||||
if (form.submit)
|
||||
form.submit();
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function set_password(form, field, password, do_submit)
|
||||
@@ -236,7 +234,7 @@ function set_password(form, field, password, do_submit)
|
||||
if (do_submit)
|
||||
{
|
||||
// Propagate change
|
||||
change_cb = field.onchange;
|
||||
var change_cb = field.onchange;
|
||||
if (change_cb)
|
||||
change_cb();
|
||||
// Try to type "enter"
|
||||
@@ -245,7 +243,8 @@ function set_password(form, field, password, do_submit)
|
||||
evt.which = 13;
|
||||
field.dispatchEvent(evt);
|
||||
// Submit form
|
||||
form.submit();
|
||||
if (form.submit)
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +271,7 @@ function block_all_forms(doc, do_block)
|
||||
{
|
||||
if (do_block)
|
||||
{
|
||||
old_cb = form.onsubmit;
|
||||
var old_cb = form.onsubmit;
|
||||
if (old_cb)
|
||||
form.removeEventListener("submit", old_cb);
|
||||
form.addEventListener("submit", on_sumbit);
|
||||
@@ -325,13 +324,13 @@ browser.runtime.onMessage.addListener(
|
||||
debug("getUsername");
|
||||
if (managed_forms.length == 1)
|
||||
{
|
||||
fields = managed_forms[0].getElementsByTagName("input");
|
||||
var fields = managed_forms[0].getElementsByTagName("input");
|
||||
|
||||
type_filters = new Array();
|
||||
var 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);
|
||||
var logins = try_get_name(fields, type_filters, true);
|
||||
|
||||
if (logins.length == 1)
|
||||
sendResponse(logins[0]);
|
||||
@@ -347,12 +346,12 @@ browser.runtime.onMessage.addListener(
|
||||
var response = "";
|
||||
if (managed_forms.length == 1)
|
||||
{
|
||||
fields = managed_forms[0].getElementsByTagName("input");
|
||||
password_field = null;
|
||||
var fields = managed_forms[0].getElementsByTagName("input");
|
||||
var password_field = null;
|
||||
|
||||
for (a=0; a<fields.length; a++)
|
||||
for (var a=0; a<fields.length; a++)
|
||||
{
|
||||
field = fields[a];
|
||||
var field = fields[a];
|
||||
if (field.getAttribute("type") == "password")
|
||||
{
|
||||
if (password_field == null)
|
||||
@@ -369,12 +368,11 @@ browser.runtime.onMessage.addListener(
|
||||
if (password_field)
|
||||
{
|
||||
set_password(managed_forms[0], password_field,
|
||||
request.password, request.submit);
|
||||
request.password, request.submit);
|
||||
response = "ok";
|
||||
}
|
||||
}
|
||||
sendResponse(response);
|
||||
return true;
|
||||
}
|
||||
else if (request.type == "blockForms")
|
||||
{
|
||||
@@ -384,11 +382,13 @@ browser.runtime.onMessage.addListener(
|
||||
{
|
||||
unblock_all_forms();
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
function document_loaded()
|
||||
{
|
||||
parameters = {
|
||||
var parameters = {
|
||||
"type": "is_gpass_enabled",
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2013-2020 Grégory Soutadé
|
||||
Copyright (C) 2013-2022 Grégory Soutadé
|
||||
|
||||
This file is part of gPass.
|
||||
|
||||
@@ -19,13 +19,12 @@
|
||||
|
||||
var DEBUG = false;
|
||||
|
||||
SERVER = {OK : 0, FAILED : 1, RESTART_REQUEST : 2};
|
||||
GPASS_ICON = {NORMAL:0, DISABLED:1, ACTIVATED:2};
|
||||
export const SERVER = {OK : 0, FAILED : 1, RESTART_REQUEST : 2};
|
||||
export const GPASS_ICON = {NORMAL:0, DISABLED:1, ACTIVATED:2};
|
||||
|
||||
var browser = browser || chrome;
|
||||
var crypto = crypto || window.crypto;
|
||||
export var browser = browser || chrome;
|
||||
|
||||
function notify(text, data)
|
||||
export function notify(text, data)
|
||||
{
|
||||
browser.runtime.sendMessage({type: "notification", options:{"message":text, "data":data}});
|
||||
}
|
||||
@@ -54,9 +53,9 @@ function str2ab(str) {
|
||||
return bufView;
|
||||
}
|
||||
|
||||
function crypto_pbkdf2(mkey, salt, level)
|
||||
export function crypto_pbkdf2(crypto, mkey, salt, level)
|
||||
{
|
||||
AESCBC = {
|
||||
var AESCBC = {
|
||||
name: "AES-CBC",
|
||||
length: 256,
|
||||
}
|
||||
@@ -83,9 +82,9 @@ function crypto_pbkdf2(mkey, salt, level)
|
||||
});
|
||||
}
|
||||
|
||||
function simple_pbkdf2(mkey, salt, level)
|
||||
export function simple_pbkdf2(crypto, mkey, salt, level)
|
||||
{
|
||||
AESCBC = {
|
||||
var AESCBC = {
|
||||
name: "AES-CBC",
|
||||
length: 256,
|
||||
}
|
||||
@@ -122,7 +121,7 @@ function _encrypt(mkey, iv, data)
|
||||
|
||||
data = str2ab(data);
|
||||
|
||||
promise = mkey.then(function(mkey){
|
||||
var promise = mkey.then(function(mkey){
|
||||
return crypto.subtle.encrypt({
|
||||
name: "AES-CBC",
|
||||
iv: iv
|
||||
@@ -142,15 +141,15 @@ async function _decrypt(mkey, iv, data)
|
||||
while ((data.length % 16))
|
||||
data += "\0";
|
||||
|
||||
nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
pkcs7_padding = new Uint8Array([16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16]);
|
||||
var nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
var pkcs7_padding = new Uint8Array([16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16]);
|
||||
pkcs7_padding = await _encrypt(mkey, nulliv, ab2str(pkcs7_padding));
|
||||
|
||||
data = str2ab(data + pkcs7_padding);
|
||||
|
||||
nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
promise = mkey.then(function(mkey){
|
||||
var promise = mkey.then(function(mkey){
|
||||
return crypto.subtle.decrypt({
|
||||
name: "AES-CBC",
|
||||
iv: iv
|
||||
@@ -165,13 +164,14 @@ async function _decrypt(mkey, iv, data)
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function encrypt_ecb(mkey, data)
|
||||
export async function encrypt_ecb(mkey, data)
|
||||
{
|
||||
var result = "";
|
||||
|
||||
var res;
|
||||
|
||||
debug("Encrypt ECB " + data);
|
||||
|
||||
nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
var nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
while (data.length > 16)
|
||||
{
|
||||
@@ -186,13 +186,14 @@ async function encrypt_ecb(mkey, data)
|
||||
return result;
|
||||
}
|
||||
|
||||
async function decrypt_ecb(mkey, data)
|
||||
export async function decrypt_ecb(mkey, data)
|
||||
{
|
||||
var result = "";
|
||||
|
||||
var res;
|
||||
|
||||
debug("Decrypt ECB " + data);
|
||||
|
||||
nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
var nulliv = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
while (data.length > 16)
|
||||
{
|
||||
@@ -207,7 +208,7 @@ async function decrypt_ecb(mkey, data)
|
||||
return result;
|
||||
}
|
||||
|
||||
async function encrypt_cbc(mkey, iv, data)
|
||||
export async function encrypt_cbc(mkey, iv, data)
|
||||
{
|
||||
debug("Encrypt CBC " + data);
|
||||
|
||||
@@ -217,7 +218,7 @@ async function encrypt_cbc(mkey, iv, data)
|
||||
return result.slice(0, result.length-16);
|
||||
}
|
||||
|
||||
async function decrypt_cbc(mkey, iv, data)
|
||||
export async function decrypt_cbc(mkey, iv, data)
|
||||
{
|
||||
debug("Decrypt CBC " + data);
|
||||
|
||||
@@ -227,22 +228,22 @@ async function decrypt_cbc(mkey, iv, data)
|
||||
return result.slice(0, result.length-16);
|
||||
}
|
||||
|
||||
async function digest(data)
|
||||
export async function digest(crypto, data)
|
||||
{
|
||||
return crypto.subtle.digest("SHA-256", str2ab(data)).then(function (hash) {
|
||||
return ab2str(hash);
|
||||
});
|
||||
}
|
||||
|
||||
function wildcard_domain(domain)
|
||||
export function wildcard_domain(domain)
|
||||
{
|
||||
var parts = domain.split(".");
|
||||
|
||||
// Standard root domain (zzz.xxx.com) or more
|
||||
if (parts.length > 2)
|
||||
{
|
||||
res = "*.";
|
||||
for (i=1; i<parts.length; i++)
|
||||
var res = "*.";
|
||||
for (var i=1; i<parts.length; i++)
|
||||
res += parts[i] + ".";
|
||||
return res.substr(0, res.length-1);
|
||||
}
|
||||
@@ -254,14 +255,14 @@ function wildcard_domain(domain)
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/3745666/how-to-convert-from-hex-to-ascii-in-javascript
|
||||
function hex2a(hex) {
|
||||
export function hex2a(hex) {
|
||||
var str = '';
|
||||
for (var i = 0; i < hex.length; i += 2)
|
||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
return str;
|
||||
}
|
||||
|
||||
function a2hex(_str_) {
|
||||
export function a2hex(_str_) {
|
||||
var hex = '';
|
||||
for (var i = 0; i < _str_.length; i++)
|
||||
{
|
||||
@@ -272,8 +273,9 @@ function a2hex(_str_) {
|
||||
return hex;
|
||||
}
|
||||
|
||||
function debug(s)
|
||||
export function debug(s)
|
||||
{
|
||||
if (DEBUG)
|
||||
console.log(s);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,31 +2,29 @@
|
||||
// (c) Steven Levithan <stevenlevithan.com>
|
||||
// MIT License
|
||||
|
||||
parseURI = {
|
||||
|
||||
parseUri : function (str) {
|
||||
var o = {
|
||||
export function parseUri (str) {
|
||||
var o = {
|
||||
strictMode: false,
|
||||
key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
|
||||
q: {
|
||||
name: "queryKey",
|
||||
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
|
||||
name: "queryKey",
|
||||
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
|
||||
},
|
||||
parser: {
|
||||
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
|
||||
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
|
||||
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
|
||||
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
|
||||
}},
|
||||
m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
|
||||
uri = {},
|
||||
i = 14;
|
||||
m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
|
||||
uri = {},
|
||||
i = 14;
|
||||
|
||||
while (i--) uri[o.key[i]] = m[i] || "";
|
||||
while (i--) uri[o.key[i]] = m[i] || "";
|
||||
|
||||
uri[o.q.name] = {};
|
||||
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
|
||||
if ($1) uri[o.q.name][$1] = $2;
|
||||
});
|
||||
uri[o.q.name] = {};
|
||||
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
|
||||
if ($1) uri[o.q.name][$1] = $2;
|
||||
});
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,41 +1,43 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"manifest_version": 3,
|
||||
|
||||
"name": "gPass",
|
||||
"short_name": "gPass",
|
||||
"version": "1.1",
|
||||
"version": "1.3",
|
||||
"description": "gPass : global password manager",
|
||||
"icons" : {"16":"icons/gpass_icon_16.png", "32":"icons/gpass_icon_32.png", "64":"icons/gpass_icon_64.png", "128":"icons/gpass_icon_128.png"},
|
||||
"author" : "Grégory Soutadé",
|
||||
"homepage_url" : "http://indefero.soutade.fr/p/gpass",
|
||||
"homepage_url" : "https://forge.soutade.fr/soutade/gPass",
|
||||
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["lib/misc.js", "lib/main.js"],
|
||||
"js": ["lib/main.js"],
|
||||
"run_at" : "document_idle",
|
||||
"all_frames" : true
|
||||
}
|
||||
],
|
||||
|
||||
"background": {
|
||||
"persistent": true,
|
||||
"scripts": ["lib/parseuri.js", "lib/misc.js", "compat.js", "background.js"]
|
||||
"service_worker": "background.js",
|
||||
"type": "module"
|
||||
},
|
||||
|
||||
"options_page": "options.html",
|
||||
|
||||
"browser_action": {
|
||||
"action": {
|
||||
"default_icon": {"32":"icons/gpass_icon_32.png"},
|
||||
"default_title": "Get your password",
|
||||
"default_popup": "popup/popup.html"
|
||||
},
|
||||
|
||||
"host_permissions": [
|
||||
"<all_urls>"
|
||||
],
|
||||
|
||||
"permissions": [
|
||||
"<all_urls>",
|
||||
"activeTab",
|
||||
"notifications",
|
||||
"tabs",
|
||||
"storage",
|
||||
"clipboardWrite",
|
||||
"contextMenus"
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
Copy password into clipboard <input id="clipboard" type="checkbox"/><br />
|
||||
<input id="getButton" type="submit" value="Get"/> <a id="serverLink" href="">Your server</a>
|
||||
</form>
|
||||
<script src="misc.js"></script>
|
||||
<script src="compat.js"></script>
|
||||
<script src="popup.js"></script>
|
||||
<script type="module" src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (C) 2020 Grégory Soutadé
|
||||
Copyright (C) 2020-2022 Grégory Soutadé
|
||||
|
||||
This file is part of gPass.
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
along with gPass. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {SERVER, browser, notify, debug} from "./misc.js";
|
||||
import {get_preference, send_tab_message} from "./compat.js";
|
||||
|
||||
var username_filled = false;
|
||||
|
||||
function _server_response(response, tabs, do_submit, force_copy)
|
||||
{
|
||||
{
|
||||
debug("Get Response");
|
||||
if (response.value == SERVER.OK)
|
||||
{
|
||||
@@ -29,17 +32,17 @@ function _server_response(response, tabs, do_submit, force_copy)
|
||||
{
|
||||
navigator.clipboard.writeText(response.password).then(function() {
|
||||
notify("Password pasted into clipboard", "");
|
||||
window.close();
|
||||
window.setTimeout(function() {window.close();}, 2000);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Fill + optional copy */
|
||||
parameters = {
|
||||
var parameters = {
|
||||
"type":"setPassword",
|
||||
"password":response.password,
|
||||
"submit":do_submit
|
||||
"submit":do_submit,
|
||||
};
|
||||
send_tab_message(tabs[0].id, parameters,
|
||||
function(arg)
|
||||
@@ -49,13 +52,13 @@ function _server_response(response, tabs, do_submit, force_copy)
|
||||
{
|
||||
navigator.clipboard.writeText(response.password).then(function() {
|
||||
notify("Password pasted into clipboard", "");
|
||||
window.close();
|
||||
window.setTimeout(function() {window.close();}, 2000);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
notify("Password filled", "");
|
||||
window.close();
|
||||
window.setTimeout(function() {window.close();}, 2000);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -91,7 +94,7 @@ function _query_tabs_get_password(tabs)
|
||||
var logins = new Array();
|
||||
logins.push(username);
|
||||
|
||||
parameters = {
|
||||
var parameters = {
|
||||
type:"password",
|
||||
logins:logins,
|
||||
domain:domain,
|
||||
@@ -115,12 +118,12 @@ function get_password(evt)
|
||||
|
||||
evt.preventDefault();
|
||||
|
||||
browser.tabs.query({active:true, currentWindow:true}, _query_tabs_get_password);
|
||||
browser.tabs.query({active:true, currentWindow:true}).then(_query_tabs_get_password);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
pform = document.getElementById("passwordForm");
|
||||
var pform = document.getElementById("passwordForm");
|
||||
|
||||
if (pform != null)
|
||||
pform.onsubmit = get_password;
|
||||
@@ -132,7 +135,7 @@ function _query_tabs_init(tabs)
|
||||
if (tabs.length != 1) return;
|
||||
|
||||
/* Fill username */
|
||||
parameters = {
|
||||
var parameters = {
|
||||
"type":"getUsername"
|
||||
};
|
||||
|
||||
@@ -166,20 +169,21 @@ function _query_tabs_init(tabs)
|
||||
};
|
||||
|
||||
browser.runtime.sendMessage(parameters, {},
|
||||
function (response)
|
||||
{
|
||||
url = response.value;
|
||||
url = url.substring(0, url.lastIndexOf('/'));
|
||||
url += '?';
|
||||
url += 'url=' + encodeURI(tabs[0].url.split("?")[0]);
|
||||
url += '&user=' + document.getElementById("gPassUsername").value;
|
||||
link = document.getElementById("serverLink");
|
||||
link.href = url;
|
||||
|
||||
return true;
|
||||
});
|
||||
function (response)
|
||||
{
|
||||
if (tabs[0].url === undefined) return true;
|
||||
if (tabs[0].url.startsWith("chrome://newtab/")) tabs[0].url = "";
|
||||
var url = response.value;
|
||||
url = url.substring(0, url.lastIndexOf('/'));
|
||||
url += '?';
|
||||
url += 'url=' + encodeURI(tabs[0].url.split("?")[0]);
|
||||
url += '&user=' + document.getElementById("gPassUsername").value;
|
||||
var link = document.getElementById("serverLink");
|
||||
link.href = url;
|
||||
return true;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
browser.tabs.query({active:true, currentWindow:true}, _query_tabs_init);
|
||||
browser.tabs.query({active:true, currentWindow:true}).then(_query_tabs_init);
|
||||
|
||||
@@ -27,10 +27,10 @@ function _notification(message, data)
|
||||
message += data;
|
||||
|
||||
options = {
|
||||
type: "basic",
|
||||
title : "gPass",
|
||||
type: "basic",
|
||||
title : "gPass",
|
||||
message : message,
|
||||
iconUrl:browser.extension.getURL("icons/gpass_icon_64.png")
|
||||
iconUrl: browser.runtime.getURL("icons/gpass_icon_64.png")
|
||||
};
|
||||
|
||||
browser.notifications.create("gPass", options, function(){});
|
||||
|
||||
@@ -25,10 +25,10 @@ function _notification(message, data)
|
||||
message += data;
|
||||
|
||||
options = {
|
||||
type: "basic",
|
||||
title : "gPass",
|
||||
type: "basic",
|
||||
title : "gPass",
|
||||
message : message,
|
||||
iconUrl:browser.extension.getURL("icons/gpass_icon_64.png")
|
||||
iconUrl: browser.runtime.getURL("icons/gpass_icon_64.png")
|
||||
};
|
||||
|
||||
browser.notifications.create(options).then(
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
"name": "gPass",
|
||||
"short_name": "gPass",
|
||||
"version": "1.1",
|
||||
"version": "1.3",
|
||||
"description": "gPass : global password manager",
|
||||
"icons" : {"16":"icons/gpass_icon_16.png", "32":"icons/gpass_icon_32.png", "64":"icons/gpass_icon_64.png", "128":"icons/gpass_icon_128.png"},
|
||||
"author" : "Grégory Soutadé",
|
||||
"homepage_url" : "http://indefero.soutade.fr/p/gpass",
|
||||
"homepage_url" : "https://forge.soutade.fr/soutade/gPass",
|
||||
|
||||
"content_scripts": [
|
||||
{
|
||||
|
||||
@@ -73,8 +73,8 @@ else
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
|
||||
<link rel="icon" type="image/png" href="resources/favicon.png" />
|
||||
<link rel="stylesheet" type="text/css" href="resources/gpass.css" />
|
||||
<link rel="icon" type="image/png" href="/resources/favicon.png" />
|
||||
<link rel="stylesheet" type="text/css" href="/resources/gpass.css" />
|
||||
<script language="javascript">
|
||||
<?php
|
||||
echo "pbkdf2_level=$PBKDF2_LEVEL; use_shadow_logins=$USE_SHADOW_LOGINS;\n";
|
||||
@@ -95,7 +95,41 @@ else
|
||||
|
||||
setTimeout(scrollToTop, 24);
|
||||
}
|
||||
</script>
|
||||
|
||||
function enableMenu(elem, divFrom)
|
||||
{
|
||||
elem.style.display = "block";
|
||||
divFrom.style['font-weight'] = "bold";
|
||||
divFrom.style['text-decoration'] = "underline";
|
||||
}
|
||||
|
||||
function disableMenu(elem, divFrom)
|
||||
{
|
||||
elem.style.display = "none";
|
||||
divFrom.style['font-weight'] = "normal";
|
||||
divFrom.style['text-decoration'] = "";
|
||||
}
|
||||
|
||||
function switchMenuDisplay(id)
|
||||
{
|
||||
const array1 = ['admin', 'add_new_password', 'update_masterkey', 'export_database'];
|
||||
|
||||
for (const _id of array1)
|
||||
{
|
||||
elem = document.getElementById(_id);
|
||||
divFrom = document.getElementById("menu_" + _id);
|
||||
if (_id === id)
|
||||
{
|
||||
if (elem.style.display == "block")
|
||||
disableMenu(elem, divFrom);
|
||||
else
|
||||
enableMenu(elem, divFrom);
|
||||
}
|
||||
else
|
||||
disableMenu(elem, divFrom);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script src="resources/misc.js"></script>
|
||||
<script src="resources/gpass.js"></script>
|
||||
<script src="resources/pwdmeter.js"></script>
|
||||
@@ -103,9 +137,15 @@ else
|
||||
</head>
|
||||
<body onload="start();">
|
||||
<div><a id="buttonTop" class="cInvisible" onclick="scrollToTop();"></a></div>
|
||||
<div id="menu">
|
||||
<div id="logo">
|
||||
<a href="http://indefero.soutade.fr/p/gpass"><img src="resources/gpass.png" alt="logo"/></a>
|
||||
</div>
|
||||
<?php if ($ADMIN_MODE) echo "<div id=\"menu_admin\" onclick=\"switchMenuDisplay('admin');\">Create user</div>\n";?>
|
||||
<div id="menu_add_new_password" onclick="switchMenuDisplay('add_new_password');">Add a new password</div>
|
||||
<div id="menu_update_masterkey" onclick="switchMenuDisplay('update_masterkey');">Update master key</div>
|
||||
<div id="menu_export_database" onclick="switchMenuDisplay('export_database');">Export database</div>
|
||||
</div>
|
||||
|
||||
<div id="admin" <?php if (!$ADMIN_MODE) echo "style=\"display:none\"";?> >
|
||||
<form method="post">
|
||||
@@ -140,7 +180,7 @@ else
|
||||
}
|
||||
}
|
||||
echo "</select>\n";
|
||||
echo ' <b>Master key </b> <input id="master_key" type="password" onkeypress="if (event.keyCode == 13) update_master_key(true);"/>';
|
||||
echo ' <b>Master key </b> <input id="master_key" type="password" onchange="update_master_key(true);"/>';
|
||||
echo "<input type=\"button\" value=\"See\" onclick=\"update_master_key(true);\" />" . "\n";
|
||||
|
||||
if (!isset($_SERVER['HTTPS']))
|
||||
@@ -155,12 +195,12 @@ else
|
||||
|
||||
if ($user != "")
|
||||
{
|
||||
echo "<b>Add a new password</b><br/>\n";
|
||||
echo "<div class=\"title\">Add a new password</div>\n";
|
||||
|
||||
echo 'URL <input type="text" id="new_url" name="url" value="' . (filter_input(INPUT_GET, "url", FILTER_SANITIZE_SPECIAL_CHARS) ?: "") . '"/>';
|
||||
echo 'URL <input type="text" id="new_url" name="url" value="' . (parse_url(filter_input(INPUT_GET, "url", FILTER_SANITIZE_SPECIAL_CHARS))['host'] ?: "") . '"/>';
|
||||
echo 'login <input type="text" id="new_login" name="login" value="' . (filter_input(INPUT_GET, "user", FILTER_SANITIZE_SPECIAL_CHARS) ?: "") . '"/>';
|
||||
echo 'password <input id="new_password" type="text" name="password"/>';
|
||||
echo 'master key <input type="text" name="mkey" id="new_mkey" onkeypress="if (event.keyCode == 13) add_password();" onkeyup="chkPass(this.value);"/>';
|
||||
echo 'master key <input type="text" name="mkey" id="new_mkey" onchange="add_password();" onkeyup="chkPass(this.value);"/>';
|
||||
echo '<input type="button" value="Generate password" onClick="generate_password();"/>';
|
||||
echo '<input type="button" value="Generate simple password" onClick="generate_simple_password();"/>';
|
||||
echo "<input type=\"button\" name=\"add\" value=\"Add\" onclick=\"add_password();\"/>";
|
||||
@@ -170,15 +210,13 @@ if ($user != "")
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div id="passwords">
|
||||
</div>
|
||||
<div id="update_masterkey">
|
||||
<?php
|
||||
global $user;
|
||||
|
||||
if ($user != "")
|
||||
{
|
||||
echo "<b>Update Masterkey</b><br/>\n";
|
||||
echo "<div class=\"title\">Update Masterkey</div>\n";
|
||||
|
||||
echo 'Old master key <input type="text" id="oldmkey"/>';
|
||||
echo 'New master key <input type="text" id="newmkey" onkeyup="chkPass(this.value);"/>';
|
||||
@@ -192,13 +230,20 @@ if ($user != "")
|
||||
|
||||
if ($user != "")
|
||||
{
|
||||
echo "<b>Export</b><br/>\n";
|
||||
echo "<div class=\"title\">Export</div>\n";
|
||||
|
||||
echo '<input type="button" value="Export" onclick="export_database();"/>';
|
||||
echo '<a id="export_link">Download</a>';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<div id="filter">
|
||||
Filter <input id='password_filter' value=<?php echo "'" . (parse_url(filter_input(INPUT_GET, "url", FILTER_SANITIZE_SPECIAL_CHARS))['host'] ?: "") . "'" ?> onchange='password_filter_changed();'/>
|
||||
<input type="button" onclick="password_filter_changed();" value="Apply"/>
|
||||
<input type="button" onclick="document.getElementById('password_filter').value = '';password_filter_changed();" value="Clear"/>
|
||||
</div>
|
||||
<div id="passwords"></div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,34 +1,61 @@
|
||||
|
||||
body {
|
||||
background-image:linear-gradient(#0096ff 30%, white);
|
||||
background-color:#2d2e44;
|
||||
height:100%; width:100%;
|
||||
color:white;
|
||||
}
|
||||
|
||||
input {
|
||||
opacity:0.9;
|
||||
}
|
||||
#logo {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
#logo img{
|
||||
display:inline;
|
||||
width:250px;
|
||||
height:170px;
|
||||
margin-right:4%;
|
||||
}
|
||||
|
||||
#menu {
|
||||
display:block;
|
||||
margin-left:auto;
|
||||
margin-right:auto;
|
||||
margin-top:30px;
|
||||
margin-bottom:40px;
|
||||
text-align:center;
|
||||
padding-bottom:10pt;
|
||||
}
|
||||
|
||||
#menu div {
|
||||
display:inline;
|
||||
margin-right:2%;
|
||||
font-size:x-large;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size:xx-large;
|
||||
font-weight:bold;
|
||||
margin-bottom:20pt;
|
||||
}
|
||||
|
||||
#admin {
|
||||
border-style:solid;
|
||||
border-width:5px;
|
||||
border-color:red;
|
||||
padding : 15px;
|
||||
margin : 15px;
|
||||
border-radius: 25px;
|
||||
border-style:solid;
|
||||
border-width:5px;
|
||||
border-color:grey;
|
||||
display:none;
|
||||
}
|
||||
|
||||
#admin form {
|
||||
text-align : center;
|
||||
}
|
||||
|
||||
#admin input {
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
#user {
|
||||
border-style:solid;
|
||||
border-width:5px;
|
||||
border-color:green;
|
||||
padding : 15px;
|
||||
margin : 15px;
|
||||
text-align:center;
|
||||
@@ -45,10 +72,12 @@ body {
|
||||
text-align : center;
|
||||
}
|
||||
|
||||
#selected_user {
|
||||
margin-left:10px;
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
#passwords {
|
||||
border-style:solid;
|
||||
border-width:5px;
|
||||
border-color:grey;
|
||||
padding : 15px;
|
||||
margin : 15px;
|
||||
}
|
||||
@@ -57,34 +86,37 @@ body {
|
||||
padding:10px;
|
||||
text-align:center;
|
||||
font-size:xx-large;
|
||||
}
|
||||
|
||||
.hash {
|
||||
width : 700px;
|
||||
color:#de0036;
|
||||
}
|
||||
|
||||
#add_new_password {
|
||||
border-radius: 25px;
|
||||
border-style:solid;
|
||||
border-width:5px;
|
||||
border-color:blue;
|
||||
border-color:grey;
|
||||
padding : 15px;
|
||||
margin : 15px;
|
||||
display:none;
|
||||
}
|
||||
|
||||
#update_masterkey {
|
||||
border-style:solid;
|
||||
border-width:5px;
|
||||
border-color:yellow;
|
||||
padding : 15px;
|
||||
margin : 15px;
|
||||
border-radius: 25px;
|
||||
border-style:solid;
|
||||
border-width:5px;
|
||||
border-color:grey;
|
||||
display:none;
|
||||
}
|
||||
|
||||
#export_database {
|
||||
border-style:solid;
|
||||
border-width:5px;
|
||||
border-color:pink;
|
||||
padding : 15px;
|
||||
margin : 15px;
|
||||
border-radius: 25px;
|
||||
border-style:solid;
|
||||
border-width:5px;
|
||||
border-color:grey;
|
||||
display:none;
|
||||
}
|
||||
|
||||
#export_link {
|
||||
@@ -92,6 +124,10 @@ body {
|
||||
visibility:hidden;
|
||||
}
|
||||
|
||||
#filter {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.error {
|
||||
text-align:center;
|
||||
color:red;
|
||||
|
||||
@@ -121,7 +121,6 @@ function clear_form()
|
||||
inputs[i].type == "password")
|
||||
inputs[i].value = "";
|
||||
}
|
||||
chkPass("");
|
||||
}
|
||||
|
||||
function url_domain(data) {
|
||||
@@ -161,6 +160,8 @@ var current_mkey = "";
|
||||
var clearTimer = null;
|
||||
var global_iv = null;
|
||||
var server_url = window.location.href.split('?')[0];
|
||||
var clear_passwords = new Array();
|
||||
var block_filter = true;
|
||||
|
||||
function PasswordEntry (ciphered_login, ciphered_password, salt, shadow_login) {
|
||||
this.ciphered_login = ciphered_login;
|
||||
@@ -426,6 +427,51 @@ async function get_ciphered_credentials(masterkey)
|
||||
req.send("get_secure_passwords=1&user=" + current_user + "&access_tokens=" + access_tokens);
|
||||
}
|
||||
|
||||
function password_filter_changed()
|
||||
{
|
||||
if (block_filter) return;
|
||||
|
||||
filter = document.getElementById('password_filter').value ;
|
||||
if (filter !== '')
|
||||
filter = '.*' + filter + '.*';
|
||||
|
||||
password_div = document.getElementById("clear_passwords");
|
||||
password_div.removeAllChilds();
|
||||
|
||||
for (idx in clear_passwords)
|
||||
{
|
||||
div = clear_passwords[idx];
|
||||
if (typeof(div) === 'function')
|
||||
continue;
|
||||
if (filter === '')
|
||||
password_div.appendChild(div);
|
||||
else
|
||||
{
|
||||
childs = clear_passwords[idx].children;
|
||||
for (elem_idx in childs)
|
||||
{
|
||||
elem = childs[elem_idx];
|
||||
if (elem.name == 'url')
|
||||
{
|
||||
// Remove * for wildcards domains
|
||||
target_url = elem.value.replace('*', '') ;
|
||||
|
||||
try {
|
||||
if (target_url.match(filter) || filter.match(target_url))
|
||||
password_div.appendChild(div);
|
||||
}
|
||||
/* Forgive re errors */
|
||||
catch(error)
|
||||
{
|
||||
// console.log(error);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function change_master_key(warning_unciphered)
|
||||
{
|
||||
var nb_unciphered = 0;
|
||||
@@ -439,9 +485,6 @@ async function change_master_key(warning_unciphered)
|
||||
nb_unciphered++;
|
||||
}
|
||||
|
||||
if (!nb_unciphered && warning_unciphered)
|
||||
alert("No password unciphered with this master key !");
|
||||
|
||||
password_div = document.getElementById("passwords");
|
||||
password_div.removeAllChilds();
|
||||
|
||||
@@ -449,6 +492,11 @@ async function change_master_key(warning_unciphered)
|
||||
div.setAttribute("id", "nb_unciphered_passwords");
|
||||
password_div.appendChild(div);
|
||||
|
||||
div = document.createElement("div");
|
||||
div.setAttribute("id", "clear_passwords");
|
||||
password_div.appendChild(div);
|
||||
|
||||
clear_passwords = new Array();
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
if (passwords[i].isUnciphered(current_mkey))
|
||||
@@ -494,13 +542,23 @@ async function change_master_key(warning_unciphered)
|
||||
update_button = document.createElement("input");
|
||||
update_button.setAttribute("type", "button");
|
||||
update_button.setAttribute("value", "Update");
|
||||
update_button.setAttribute("onclick", "update_entry(\"unciph_entry_" + i + "\");");
|
||||
update_button.setAttribute("onclick", "update_entry(this, \"unciph_entry_" + i + "\");");
|
||||
div.appendChild(update_button);
|
||||
|
||||
password_div.appendChild(div);
|
||||
clipboard_button = document.createElement("input");
|
||||
clipboard_button.setAttribute("type", "button");
|
||||
clipboard_button.setAttribute("name", "copy_button");
|
||||
clipboard_button.setAttribute("value", "Copy password");
|
||||
clipboard_button.setAttribute("onclick", "copy_clipboard(this, \"unciph_entry_" + i + "\");");
|
||||
div.appendChild(clipboard_button);
|
||||
|
||||
clear_passwords.push(div);
|
||||
}
|
||||
}
|
||||
|
||||
if (warning_unciphered && !nb_unciphered)
|
||||
alert("No password unciphered with this master key !");
|
||||
|
||||
div = document.createElement("div");
|
||||
div.setAttribute("id", "nb_ciphered_passwords");
|
||||
password_div.appendChild(div);
|
||||
@@ -523,13 +581,15 @@ async function change_master_key(warning_unciphered)
|
||||
url.setAttribute("class", "hash");
|
||||
url.setAttribute("type", "text");
|
||||
url.setAttribute("name", "URL");
|
||||
url.setAttribute("size", "32");
|
||||
div.appendChild(url);
|
||||
|
||||
div.appendChild(document.createTextNode("password"));
|
||||
password = document.createElement("input");
|
||||
password.setAttribute("class", "hash");
|
||||
password.setAttribute("class", "password_hash");
|
||||
password.setAttribute("type", "text");
|
||||
password.setAttribute("name", "password");
|
||||
password.setAttribute("size", "40");
|
||||
div.appendChild(password);
|
||||
|
||||
delete_button = document.createElement("input");
|
||||
@@ -555,41 +615,12 @@ async function change_master_key(warning_unciphered)
|
||||
}
|
||||
}
|
||||
|
||||
cur_url = document.getElementById("new_url").value;
|
||||
|
||||
/* If we have a current URL in add form and we have a password entry that match this URL, go to the last */
|
||||
/* Can't do this before, because everything is not displayed from browser */
|
||||
if (cur_url !== "")
|
||||
{
|
||||
cur_url = url_domain(cur_url);
|
||||
for(i=0; i<passwords.length; i++)
|
||||
{
|
||||
if (!passwords[i].isUnciphered(current_mkey))
|
||||
continue;
|
||||
url_elem = document.getElementById("unciph_url_" + i);
|
||||
target_url = url_elem.value;
|
||||
// Replace wildcard domain by .*<domain>
|
||||
if (target_url[0] == "*")
|
||||
target_url = "." + target_url;
|
||||
try {
|
||||
if (cur_url.match(target_url))
|
||||
{
|
||||
window.scrollTo(0, url_elem.offsetTop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Forgive re errors */
|
||||
catch(error)
|
||||
{
|
||||
//console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input = document.getElementById("master_key");
|
||||
input.value = "";
|
||||
|
||||
update_stats();
|
||||
block_filter = false;
|
||||
password_filter_changed();
|
||||
}
|
||||
|
||||
function update_master_key(warning_unciphered)
|
||||
@@ -637,7 +668,11 @@ function update_master_key(warning_unciphered)
|
||||
|
||||
function start()
|
||||
{
|
||||
select_widget = document.getElementById('selected_user') ;
|
||||
new_url = document.getElementById('new_url') ;
|
||||
if (new_url.value !== "")
|
||||
switchMenuDisplay('add_new_password');
|
||||
|
||||
select_widget = document.getElementById('selected_user') ;
|
||||
|
||||
if (select_widget == null) return;
|
||||
|
||||
@@ -777,16 +812,14 @@ function add_password()
|
||||
|
||||
for(i=0; i<inputs.length; i++)
|
||||
{
|
||||
if (inputs[i].getAttribute("type") == "text" ||
|
||||
inputs[i].getAttribute("type") == "password")
|
||||
name = inputs[i].getAttribute("name");
|
||||
if (name === "password" || name === "mkey")
|
||||
inputs[i].value = "";
|
||||
}
|
||||
|
||||
startClearTimer();
|
||||
});
|
||||
|
||||
window.scrollTo(0,document.body.scrollHeight);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -851,7 +884,7 @@ function delete_entry(entry_number)
|
||||
update_stats();
|
||||
}
|
||||
|
||||
function update_entry(entry_number)
|
||||
function update_entry(button, entry_number)
|
||||
{
|
||||
var url = "";
|
||||
var login = "";
|
||||
@@ -915,7 +948,14 @@ function update_entry(entry_number)
|
||||
passwords[found] = pentry;
|
||||
ciphered_login.setAttribute("login", pentry.ciphered_login);
|
||||
|
||||
alert("Entry updated");
|
||||
if (button !== null)
|
||||
{
|
||||
old_value = button.value;
|
||||
button.value = "Updated";
|
||||
window.setTimeout(() => { button.value = old_value; }, 2000);
|
||||
}
|
||||
else
|
||||
alert("Entry updated");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -982,6 +1022,39 @@ async function update_masterkey()
|
||||
}
|
||||
}
|
||||
|
||||
function copy_clipboard(button, entry_number)
|
||||
{
|
||||
var password = "";
|
||||
|
||||
entry = document.getElementById(entry_number);
|
||||
|
||||
if (entry == null) {
|
||||
alert(entry_number + " not found");
|
||||
return;
|
||||
}
|
||||
|
||||
inputs = entry.getElementsByTagName("input");
|
||||
|
||||
for(i=0; i<inputs.length; i++)
|
||||
{
|
||||
if (inputs[i].getAttribute("name") == "password")
|
||||
{
|
||||
inputs[i].select();
|
||||
document.execCommand('copy');
|
||||
inputs[i].setSelectionRange(0,0); // Unselect
|
||||
if (button !== null)
|
||||
{
|
||||
old_value = button.value;
|
||||
button.value = "Copied";
|
||||
window.setTimeout(() => { button.value = old_value; }, 2000);
|
||||
}
|
||||
else
|
||||
alert('Password copied into clipboard');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makeText(text) {
|
||||
var data = new Blob([text], {type: 'application/xml'});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user