Merge branch 'develop'

This commit is contained in:
varjolintu 2017-05-27 15:09:51 +03:00
commit 9bd870ae35
16 changed files with 352 additions and 227 deletions

44
CHANGELOG Normal file
View file

@ -0,0 +1,44 @@
0.1.6 (2017-05-27)
=========================
- Upgraded tweetnacl-js to 1.0.0
- Upgraded tweetnacl-utils-js to 0.15.0
- Some code fixes concerning encryption and decryption
- Redesigned simpler password generator dialog
0.1.5 (2017-05-22)
=========================
- Fixed a few deprecated functions
- Added some more Firefox compatible code (Firefox now works 90%!)
- Removed an unncecessary .map file
0.1.4 (2017-05-21)
=========================
- Upgraded manifest options to V2
- Added some more Firefox compatible code
0.1.3 (2017-05-19)
=========================
- Fixed a bug showing correct status in the popup
- Added a license for a quick method to determine which browser is used in API calls
0.1.2 (2017-05-18)
=========================
- Upgraded jquery from 3.2.0 to 3.2.1
- Removed unnecessary images
- Upgraded deprecated API calls (extension -> runtime, so from synchronous to asynchronous)
- Partial Firefox support (the extension can be loaded but functionality is still limited)
0.1.1 (2017-04-28)
=========================
- This version works with the KeePassXC fork
- Upgraded JavaScripts to work asynchronously
0.1.0 (2017-04-12)
=========================
- Replaced crypto libraries with tweetnacl-js
- New application and popup icons
- Upgraded bootstrap to version 3.3.7
- Upgraded jquery from 1.11 to 3.2.0
- Upgraded jquery-ui from 1.10.2 to 1.12.1

View file

@ -2,6 +2,7 @@
Chrome extension for [KeePassXC](https://keepassxc.org/) with Native Messaging.
This is a heavily forked version of [pfn](https://github.com/pfn)'s [chromeIPass](https://github.com/pfn/passifox).
For testing purposes, please use following unofficial KeePassXC [release's](https://github.com/varjolintu/keepassxc/releases).
Get the extension for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/chromekeepassxc/) or [Chrome/Chromium](https://chrome.google.com/webstore/detail/chromekeepassxc/iopaggbpplllidnfmcghoonnokmjoicf).

View file

@ -10,7 +10,7 @@ httpAuth.proxyUrl = null;
httpAuth.handleRequest = function (details, callback) {
if(httpAuth.requestId == details.requestId || !page.tabs[details.tabId]) {
if (httpAuth.requestId == details.requestId || !page.tabs[details.tabId]) {
callback({});
}
else {
@ -26,7 +26,7 @@ httpAuth.processPendingCallbacks = function(details) {
httpAuth.url = details.url;
httpAuth.isProxy = details.isProxy;
if(details.challenger){
if (details.challenger){
httpAuth.proxyUrl = details.challenger.host;
}
@ -46,7 +46,7 @@ httpAuth.loginOrShowCredentials = function(logins) {
event.onHTTPAuthPopup(null, {"id": httpAuth.tabId}, {"logins": logins, "url": url});
//generate popup-list for HTTP Auth usernames + descriptions
if(page.settings.autoFillAndSend) {
if (page.settings.autoFillAndSend) {
httpAuth.callback({
authCredentials: {
username: logins[0].login,

View file

@ -105,7 +105,7 @@ browser.runtime.onMessage.addListener(event.onMessage);
* Add context menu entry for filling in username + password
*/
browser.contextMenus.create({
"title": "Fill &User + Pass",
"title": "Fill User + Pass",
"contexts": [ "editable" ],
"onclick": function(info, tab) {
browser.tabs.sendMessage(tab.id, {
@ -118,7 +118,7 @@ browser.contextMenus.create({
* Add context menu entry for filling in only password which matches for given username
*/
browser.contextMenus.create({
"title": "Fill &Pass Only",
"title": "Fill Pass Only",
"contexts": [ "editable" ],
"onclick": function(info, tab) {
browser.tabs.sendMessage(tab.id, {
@ -131,7 +131,7 @@ browser.contextMenus.create({
* Add context menu entry for creating icon for generate-password dialog
*/
browser.contextMenus.create({
"title": "Show Password &Generator Icons",
"title": "Show Password Generator Icons",
"contexts": [ "editable" ],
"onclick": function(info, tab) {
browser.tabs.sendMessage(tab.id, {
@ -144,7 +144,7 @@ browser.contextMenus.create({
* Add context menu entry for creating icon for generate-password dialog
*/
browser.contextMenus.create({
"title": "&Save credentials",
"title": "Save credentials",
"contexts": [ "editable" ],
"onclick": function(info, tab) {
browser.tabs.sendMessage(tab.id, {

View file

@ -76,7 +76,7 @@ keepass.updateCredentials = function(callback, tab, entryId, username, password,
keepass.callbackOnId(keepass.nativePort.onMessage, "set-login", function(response) {
if (response.message && response.nonce) {
var res = keepass.decrypt(response.message, response.nonce);
if (res == false)
if (!res)
{
console.log("Failed to decrypt message");
}
@ -146,7 +146,7 @@ keepass.retrieveCredentials = function (callback, tab, url, submiturl, forceCall
keepass.callbackOnId(keepass.nativePort.onMessage, "get-logins", function(response) {
if (response.message && response.nonce) {
var res = keepass.decrypt(response.message, response.nonce);
if (res == false)
if (!res)
{
console.log("Failed to decrypt message");
}
@ -227,7 +227,7 @@ keepass.generatePassword = function (callback, tab, forceCallback) {
keepass.callbackOnId(keepass.nativePort.onMessage, "generate-password", function(response) {
if (response.message && response.nonce) {
var res = keepass.decrypt(response.message, response.nonce);
if (res == false)
if (!res)
{
console.log("Failed to decrypt message");
}
@ -309,7 +309,7 @@ keepass.associate = function(callback, tab) {
keepass.callbackOnId(keepass.nativePort.onMessage, "associate", function(response) {
if (response.message && response.nonce) {
var res = keepass.decrypt(response.message, response.nonce);
if (res == false)
if (!res)
{
console.log("Failed to decrypt message");
}
@ -390,7 +390,7 @@ keepass.testAssociation = function (callback, tab, triggerUnlock) {
id: id,
key: idkey
};
console.log(messageData);
var request = {
action: "test-associate",
message: keepass.encrypt(messageData, nonce),
@ -400,7 +400,7 @@ keepass.testAssociation = function (callback, tab, triggerUnlock) {
keepass.callbackOnId(keepass.nativePort.onMessage, "test-associate", function(response) {
if (response.message && response.nonce) {
var res = keepass.decrypt(response.message, response.nonce);
if (res == false) {
if (!res) {
console.log("Failed to decrypt message");
}
else
@ -606,10 +606,6 @@ keepass.deleteKey = function(hash) {
localStorage.keyRing = JSON.stringify(keepass.keyRing);
}
keepass.getIconColor = function() {
return ((keepass.databaseHash in keepass.keyRing) && keepass.keyRing[keepass.databaseHash].icon) ? keepass.keyRing[keepass.databaseHash].icon : "blue";
}
keepass.setcurrentKeePassXCVersion = function(version) {
if (version) {
keepass.currentKeePassXC = {
@ -770,6 +766,9 @@ keepass.setCryptoKey = function(id, key) {
keepass.encrypt = function(input, nonce) {
var messageData = nacl.util.decodeUTF8(JSON.stringify(input));
var message = nacl.box(messageData, nonce, keepass.serverPublicKey, keepass.keyPair.secretKey);
if (!message) {
return "";
}
return keepass.b64e(message);
}

View file

@ -1,51 +0,0 @@
// Written in 2014-2016 by Dmitry Chestnykh and Devi Mandiri.
// Public domain.
(function(root, f) {
'use strict';
if (typeof module !== 'undefined' && module.exports) module.exports = f();
else if (root.nacl) root.nacl.util = f();
else {
root.nacl = {};
root.nacl.util = f();
}
}(this, function() {
'use strict';
var util = {};
util.decodeUTF8 = function(s) {
if (typeof s !== 'string') throw new TypeError('expected string');
var i, d = unescape(encodeURIComponent(s)), b = new Uint8Array(d.length);
for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
return b;
};
util.encodeUTF8 = function(arr) {
var i, s = [];
for (i = 0; i < arr.length; i++) s.push(String.fromCharCode(arr[i]));
return decodeURIComponent(escape(s.join('')));
};
util.encodeBase64 = function(arr) {
if (typeof btoa === 'undefined') {
return (new Buffer(arr)).toString('base64');
} else {
var i, s = [], len = arr.length;
for (i = 0; i < len; i++) s.push(String.fromCharCode(arr[i]));
return btoa(s.join(''));
}
};
util.decodeBase64 = function(s) {
if (typeof atob === 'undefined') {
return new Uint8Array(Array.prototype.slice.call(new Buffer(s, 'base64'), 0));
} else {
var i, d = atob(s), b = new Uint8Array(d.length);
for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
return b;
}
};
return util;
}));

1
chromeKeePassXC/background/nacl-util.min.js vendored Executable file
View file

@ -0,0 +1 @@
!function(e,n){"use strict";"undefined"!=typeof module&&module.exports?module.exports=n():e.nacl?e.nacl.util=n():(e.nacl={},e.nacl.util=n())}(this,function(){"use strict";function e(e){if(!/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/.test(e))throw new TypeError("invalid encoding")}var n={};return n.decodeUTF8=function(e){if("string"!=typeof e)throw new TypeError("expected string");var n,r=unescape(encodeURIComponent(e)),t=new Uint8Array(r.length);for(n=0;n<r.length;n++)t[n]=r.charCodeAt(n);return t},n.encodeUTF8=function(e){var n,r=[];for(n=0;n<e.length;n++)r.push(String.fromCharCode(e[n]));return decodeURIComponent(escape(r.join("")))},"undefined"==typeof atob?"undefined"!=typeof Buffer.from?(n.encodeBase64=function(e){return Buffer.from(e).toString("base64")},n.decodeBase64=function(n){return e(n),new Uint8Array(Array.prototype.slice.call(Buffer.from(n,"base64"),0))}):(n.encodeBase64=function(e){return new Buffer(e).toString("base64")},n.decodeBase64=function(n){return e(n),new Uint8Array(Array.prototype.slice.call(new Buffer(n,"base64"),0))}):(n.encodeBase64=function(e){var n,r=[],t=e.length;for(n=0;n<t;n++)r.push(String.fromCharCode(e[n]));return btoa(r.join(""))},n.decodeBase64=function(n){e(n);var r,t=atob(n),o=new Uint8Array(t.length);for(r=0;r<t.length;r++)o[r]=t.charCodeAt(r);return o}),n});

View file

@ -19,19 +19,19 @@ page.initSettings = function() {
page.settings.checkUpdateKeePassXC = 3;
}
if (!("autoCompleteUsernames" in page.settings)) {
page.settings.autoCompleteUsernames = 1;
page.settings.autoCompleteUsernames = true;
}
if (!("autoFillAndSend" in page.settings)) {
page.settings.autoFillAndSend = 1;
page.settings.autoFillAndSend = true;
}
if (!("usePasswordGenerator" in page.settings)) {
page.settings.usePasswordGenerator = 1;
page.settings.usePasswordGenerator = true;
}
if (!("autoFillSingleEntry" in page.settings)) {
page.settings.autoFillSingleEntry = 0;
page.settings.autoFillSingleEntry = false;
}
if (!("autoRetrieveCredentials" in page.settings)) {
page.settings.autoRetrieveCredentials = 1;
page.settings.autoRetrieveCredentials = true;
}
localStorage.settings = JSON.stringify(page.settings);
}

View file

@ -6,38 +6,65 @@
font-family: Verdana, Arial, sans-serif !important;
color: #222222 !important;
}
.ui-dialog-titlebar-close {
visibility: hidden !important;
}
.cip-ui-widget-overlay {
background: none !important;
z-index: 2147483601 !important;
}
.cip-ui-dialog {
z-index: 2147483602 !important;
}
.cip-ui-dialog .cip-ui-dialog-title {
font-size:.8em !important;
}
.cip-ui-dialog .cip-ui-dialog-titlebar {
padding:.1em .5em !important;
}
.cip-ui-dialog-content {
font-size: .8em !important;
.ui-dialog {
font-size: 12px !important;
}
#cip-genpw-dialog {
text-align: left !important;
.dialog-form .ui-dialog-content .ui-widget-content {
max-height: 80px !important;
}
#cip-genpw-dialog button {
height: 26px !important;
.ui-dialog-titlebar {
background-color: #3a8233;
color: #fff;
}
.cip-genpw-clearfix:after {
clear: both;
line-height: 0;
content: "";
.ui-dialog .ui-dialog-buttonpane {
text-align: center !important;
}
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
float: none !important;
}
.ui-button .ui-button-text .ui-button {
font-size: .10em !important;
}
input.genpw-text {
font-size: .9em !important;
padding: .4em;
border-radius: 4px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-collapse: separate;
width: 100%;
}
.genpw-input-group-addon {
font-size: inherit !important;
background-color: #eee;
border: 1px solid #ccc;
padding: .4em;
border-radius: 4px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
white-space: nowrap;
vertical-align: middle;
display: table-cell;
border-collapse: separate;
}
.genpw-input-group {
position: relative;
display: table;
border-collapse: separate;
width: 100%;
}
.cip-genpw-icon {
position: absolute;
cursor: pointer;
@ -62,38 +89,7 @@
height: 24px;
background-image: url(moz-extension://__MSG_@@extension_id__/icons/key_24x24.png);
}
.cip-genpw-password-frame {
margin-top: 5px !important;
margin-bottom: 5px !important;
}
.cip-genpw-password-frame > * {
height: 20px !important;
font-size: 11px !important;
}
.cip-genpw-textfield {
background: none !important;
font-size: 11px !important;
display: inline !important;
border: 1px solid rgb(170, 170, 170) !important;
padding: 1px 2px !important;
max-width: none !important;
min-width: 0 !important;
font-size: 1em !important;
width: 240px !important;
padding-left: 5px !important;
}
#cip-genpw-quality {
width: 50px !important;
padding-top: 1px !important;
padding-bottom: 1px !important;
}
.cip-genpw-label {
display: block !important;
margin: 5px 0 !important;
}
.cip-genpw-checkbox {
vertical-align: middle !important;
}
#cip-genpw-btn-fillin {
margin-right: 5px;
}

View file

@ -9,6 +9,9 @@ window.browser = (function () {
window.chrome;
})();
// Initialize autocomplete feature
$(this.target).find('input').autocomplete();
// contains already called method names
var _called = {};
@ -87,7 +90,7 @@ var cipAutocomplete = {};
cipAutocomplete.elements = [];
cipAutocomplete.init = function(field) {
if(field.hasClass("ui-autocomplete-input")) {
if (field.hasClass("ui-autocomplete-input")) {
//_f(credentialInputs[i].username).autocomplete("source", autocompleteSource);
field.autocomplete("destroy");
}
@ -210,59 +213,28 @@ cipPassword.createDialog = function() {
_called.passwordCreateDialog = true;
var $dialog = jQuery("<div>")
.addClass("dialog-form")
.attr("id", "cip-genpw-dialog");
var $divFloat = jQuery("<div>").addClass("cip-genpw-clearfix");
var $btnGenerate = jQuery("<button>")
.text("Generate")
.attr("id", "cip-genpw-btn-generate")
.addClass("btn")
.addClass("btn-primary")
.addClass("btn-sm")
.css("float", "left")
.click(function(e) {
e.preventDefault();
browser.runtime.sendMessage({
action: "generate_password"
}, cipPassword.callbackGeneratedPassword);
});
$divFloat.append($btnGenerate);
var $btnClipboard = jQuery("<button>")
.text("Copy to clipboard")
.attr("id", "cip-genpw-btn-clipboard")
.addClass("btn")
.addClass("btn-sm")
.css("float", "right")
.click(function(e) {
e.preventDefault();
browser.runtime.sendMessage({
action: "copy_password",
args: [jQuery("input#cip-genpw-textfield-password").val()]
}, cipPassword.callbackPasswordCopied);
});
$divFloat.append($btnClipboard);
$dialog.append($divFloat);
var $inputDiv = jQuery("<div>").addClass("form-group");
var $inputGroup = jQuery("<div>").addClass("genpw-input-group");
var $textfieldPassword = jQuery("<input>")
.attr("id", "cip-genpw-textfield-password")
.attr("type", "text")
.addClass("cip-genpw-textfield")
.attr("aria-describedby", "cip-genpw-quality")
.attr("placeholder", "Generated password")
.addClass("genpw-text ui-widget-content ui-corner-all")
.on('change keypress paste textInput input', function() {
jQuery("#cip-genpw-btn-clipboard:first").removeClass("btn-success");
});
var $quality = jQuery("<span>")
.addClass("genpw-input-group-addon")
.addClass("b2c-add-on")
.attr("id", "cip-genpw-quality")
.text("123 Bits");
var $frameInputAppend = jQuery("<div>")
.addClass("b2c-input-append")
.addClass("cip-genpw-password-frame");
$frameInputAppend.append($textfieldPassword).append($quality);
$dialog.append($frameInputAppend);
$inputGroup.append($textfieldPassword).append($quality);
var $checkGroup = jQuery("<div>").addClass("genpw-input-group");
var $checkboxNextField = jQuery("<input>")
.attr("id", "cip-genpw-checkbox-next-field")
.attr("type", "checkbox")
@ -271,59 +243,76 @@ cipPassword.createDialog = function() {
.append($checkboxNextField)
.addClass("cip-genpw-label")
.append(" also fill in the next password-field");
$dialog.append($labelNextField);
$checkGroup.append($labelNextField);
var $btnFillIn = jQuery("<button>")
.text("Fill in & copy to clipboard")
.attr("id", "cip-genpw-btn-fillin")
.addClass("btn")
.addClass("btn-sm")
.click(function(e) {
e.preventDefault();
var fieldId = jQuery("#cip-genpw-dialog:first").data("cip-genpw-field-id");
var field = jQuery("input[data-cip-id='"+fieldId+"']:first");
if (field.length == 1) {
var $password = jQuery("input#cip-genpw-textfield-password:first").val();
if (field.attr("maxlength")) {
if ($password.length > field.attr("maxlength")) {
$password = $password.substring(0, field.attr("maxlength"));
jQuery("input#cip-genpw-textfield-password:first").val($password);
jQuery("#cip-genpw-btn-clipboard:first").removeClass("b2c-btn-success");
alert("The generated password is longer than the allowed length!\nIt has been cut to fit the length.\n\nPlease remember the new password!");
}
}
field.val($password);
if (jQuery("input#cip-genpw-checkbox-next-field:checked").length == 1) {
if(field.data("cip-genpw-next-field-exists")) {
var nextFieldId = field.data("cip-genpw-next-field-id");
var nextField = jQuery("input[data-cip-id='"+nextFieldId+"']:first");
if(nextField.length == 1) {
nextField.val($password);
}
}
}
// copy password to clipboard
browser.runtime.sendMessage({
action: "copy_password",
args: [$password]
}, cipPassword.callbackPasswordCopied);
}
});
$dialog.append($btnFillIn);
$inputDiv.append($inputGroup).append($checkGroup);
$dialog.append($inputDiv);
$dialog.hide();
jQuery("body").append($dialog);
$dialog.dialog({
closeText: "×",
autoOpen: false,
modal: true,
resizable: false,
minWidth: 340,
minWidth: 300,
minHeight: 80,
title: "Password Generator",
classes: {"ui-dialog": "ui-corner-all"},
buttons: {
"Generate":
{
text: "Generate",
id: "cip-genpw-btn-generate",
click: function(e) {
e.preventDefault();
browser.runtime.sendMessage({
action: "generate_password"
}, cipPassword.callbackGeneratedPassword);
}
},
"Copy": function(e) {
e.preventDefault();
browser.runtime.sendMessage({
action: "copy_password",
args: [jQuery("input#cip-genpw-textfield-password").val()]
}, cipPassword.callbackPasswordCopied);
},
"Fill & copy": function(e) {
e.preventDefault();
var fieldId = jQuery("#cip-genpw-dialog:first").data("cip-genpw-field-id");
var field = jQuery("input[data-cip-id='"+fieldId+"']:first");
if (field.length == 1) {
var $password = jQuery("input#cip-genpw-textfield-password:first").val();
if (field.attr("maxlength")) {
if ($password.length > field.attr("maxlength")) {
$password = $password.substring(0, field.attr("maxlength"));
jQuery("input#cip-genpw-textfield-password:first").val($password);
jQuery("#cip-genpw-btn-clipboard:first").removeClass("b2c-btn-success");
alert("The generated password is longer than the allowed length!\nIt has been cut to fit the length.\n\nPlease remember the new password!");
}
}
field.val($password);
if (jQuery("input#cip-genpw-checkbox-next-field:checked").length == 1) {
if(field.data("cip-genpw-next-field-exists")) {
var nextFieldId = field.data("cip-genpw-next-field-id");
var nextField = jQuery("input[data-cip-id='"+nextFieldId+"']:first");
if(nextField.length == 1) {
nextField.val($password);
}
}
}
// Copy password to clipboard
browser.runtime.sendMessage({
action: "copy_password",
args: [$password]
}, cipPassword.callbackPasswordCopied);
}
}
},
open: function(event, ui) {
jQuery(".ui-widget-overlay").click(function() {
jQuery("#cip-genpw-dialog:first").dialog("close");

View file

@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "chromeKeePassXC",
"version": "0.1.5",
"version": "0.1.6",
"description": "KeePassXC integration for modern web browsers",
"author": "Sami Vänttinen",
"icons": {
@ -25,7 +25,7 @@
"background": {
"scripts": [
"background/nacl.min.js",
"background/nacl-util.js",
"background/nacl-util.min.js",
"background/keepass.js",
"background/httpauth.js",
"background/browserAction.js",

View file

@ -74,11 +74,6 @@ options.initGeneralSettings = function() {
}, options.showKeePassXCVersions);
});
$("#showDangerousSettings").click(function() {
$('#dangerousSettings').is(":visible") ? $(this).text("Show these settings anyway") : $(this).text("Hide");
$("#dangerousSettings").toggle();
});
$("#blinkTimeout").val(options.settings["blinkTimeout"]);
$("#blinkMinTimeout").val(options.settings["blinkMinTimeout"]);
$("#allowedRedirect").val(options.settings["allowedRedirect"]);

View file

@ -11,7 +11,7 @@
<body>
<div id="settings" class="settings">
<button id="btn-options" class="btn btn-sm btn-success">Settings</button>
<button id="btn-choose-credential-fields" class="btn btn-sm">Choose own credential fields for this page</button>
<button id="btn-choose-credential-fields" class="btn btn-sm btn-default">Choose own credential fields for this page</button>
<div id="update-available">
You use an old version of KeePassXC.
@ -62,7 +62,7 @@
connected to KeePassXC.
</p>
<div style="text-align: right">
<button id="redetect-fields-button" class="btn">Redetect credential fields</button>
<button id="redetect-fields-button" class="btn btn-default">Redetect credential fields</button>
</div>
</div>
@ -74,7 +74,7 @@
<code id="error-message"></code>
</p>
<div style="text-align: right">
<button id="reload-status-button" class="btn">Reload</button>
<button id="reload-status-button" class="btn btn-primary">Reload</button>
</div>
</div>
</body>

View file

@ -1,9 +1,10 @@
{
"name": "com.varjolintu.chromekeepassxc",
"description": "KeepassXC integration with Chrome with Native Messaging support",
"path": "<KeePassXC path here>",
"path" : "%%replace%%",
"type": "stdio",
"allowed_origins": [
"chrome-extension://ffojhfbafadajlgddbgadkbbchliloel/"
"chrome-extension://iopaggbpplllidnfmcghoonnokmjoicf/",
"chrome-extension://fhakpkpdnjecjfceboihdjpfmgajebii/"
]
}

View file

@ -0,0 +1,9 @@
{
"name": "com.varjolintu.chromekeepassxc",
"description": "KeepassXC integration with Firefox with Native Messaging support",
"path" : "%%replace%%",
"type": "stdio",
"allowed_extensions": [
"chromeKeePassXC@sami.vanttinen"
]
}

141
install.sh Executable file
View file

@ -0,0 +1,141 @@
#!/usr/bin/env bash
# The MIT License (MIT)
# Copyright (c) 2016 Danny van Kooten
# Modifications (c) 2017 Sami Vänttinen
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
# and associated documentation files (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
set -e
DIR="$( cd "$( dirname "$0" )" && pwd )"
APP_NAME="com.varjolintu.chromekeepassxc"
HOST_FILE="$DIR"
KEEPASSXC_PATH=""
# Find target dirs for various browsers & OS'es
# https://developer.chrome.com/extensions/nativeMessaging#native-messaging-host-location
# https://wiki.mozilla.org/WebExtensions/Native_Messaging
if [ $(uname -s) == 'Darwin' ]; then
if [ "$(whoami)" == "root" ]; then
TARGET_DIR_CHROME="/Library/Google/Chrome/NativeMessagingHosts"
TARGET_DIR_CHROMIUM="/Library/Application Support/Chromium/NativeMessagingHosts"
TARGET_DIR_FIREFOX="/Library/Application Support/Mozilla/NativeMessagingHosts"
TARGET_DIR_VIVALDI="/Library/Application Support/Vivaldi/NativeMessagingHosts"
else
TARGET_DIR_CHROME="$HOME/Library/Application Support/Google/Chrome/NativeMessagingHosts"
TARGET_DIR_CHROMIUM="$HOME/Library/Application Support/Chromium/NativeMessagingHosts"
TARGET_DIR_FIREFOX="$HOME/Library/Application Support/Mozilla/NativeMessagingHosts"
TARGET_DIR_VIVALDI="$HOME/Library/Application Support/Vivaldi/NativeMessagingHosts"
fi
else
if [ "$(whoami)" == "root" ]; then
TARGET_DIR_CHROME="/etc/opt/chrome/native-messaging-hosts"
TARGET_DIR_CHROMIUM="/etc/chromium/native-messaging-hosts"
TARGET_DIR_FIREFOX="/usr/lib/mozilla/native-messaging-hosts"
TARGET_DIR_VIVALDI="/etc/chromium/native-messaging-hosts"
else
TARGET_DIR_CHROME="$HOME/.config/google-chrome/NativeMessagingHosts"
TARGET_DIR_CHROMIUM="$HOME/.config/chromium/NativeMessagingHosts"
TARGET_DIR_FIREFOX="$HOME/.mozilla/native-messaging-hosts"
TARGET_DIR_VIVALDI="$HOME/.config/vivaldi/NativeMessagingHosts"
fi
fi
if [ -e "$DIR/chromeKeePassXC" ]; then
echo "Detected development binary"
HOST_FILE="$DIR/chromeKeePassXC"
fi
echo ""
echo "Select your browser:"
echo "===================="
echo "1) Chrome"
echo "2) Chromium"
echo "3) Firefox"
echo "4) Vivaldi"
echo -n "1-4: "
read BROWSER
echo ""
# Set target dir from user input
if [[ "$BROWSER" == "1" ]]; then
BROWSER_NAME="Chrome"
TARGET_DIR="$TARGET_DIR_CHROME"
fi
if [[ "$BROWSER" == "2" ]]; then
BROWSER_NAME="Chromium"
TARGET_DIR="$TARGET_DIR_CHROMIUM"
fi
if [[ "$BROWSER" == "3" ]]; then
BROWSER_NAME="Firefox"
TARGET_DIR="$TARGET_DIR_FIREFOX"
fi
if [[ "$BROWSER" == "4" ]]; then
BROWSER_NAME="Vivaldi"
TARGET_DIR="$TARGET_DIR_VIVALDI"
fi
# Try to find the KeePassXC binary.
if [ $(uname -s) == 'Darwin' ]; then
KEEPASSXC_PATH="/Applications/KeePassXC.app"
else
KEEPASSXC_PATH="$(command -v keepassxc)"
if [ -z "$KEEPASSXC_PATH" ] ; then
echo ""
echo -n "KeePassXC binary not found. Give the location of KeePassXC binary: "
read KEEPASSXC_PATH
echo ""
fi
fi
echo "KeePassXC binary location set to $KEEPASSXC_PATH"
echo "Installing $BROWSER_NAME host config with path $KEEPASSXC_PATH"
echo "Press (ENTER) or give a new path to binary if it's not correct: "
read NEW_PATH
if [ "$NEW_PATH" ]; then
KEEPASSXC_PATH="$NEW_PATH"
echo "New path set to: $KEEPASSXC_PATH"
fi
# Add /Contents/MacOS/KeePassXC to darwin for exact binary path
if [ $(uname -s) == 'Darwin' ]; then
KEEPASSXC_PATH="$KEEPASSXC_PATH/Contents/MacOS/KeePassXC"
fi
ESCAPED_PATH=${KEEPASSXC_PATH////\\/}
# Create config dir if not existing
mkdir -p "$TARGET_DIR"
# Copy manifest host config file
if [ "$BROWSER" == "1" ] || [ "$BROWSER" == "2" ] || [ "$BROWSER" == "4" ]; then
cp "$DIR/com.varjolintu.chromekeepassxc-chrome.json" "$TARGET_DIR/$APP_NAME.json"
else
cp "$DIR/com.varjolintu.chromekeepassxc-firefox.json" "$TARGET_DIR//$APP_NAME.json"
fi
# Replace path to host
if [ $(uname -s) == 'Darwin' ]; then
sed -i "" -e "s/%%replace%%/$ESCAPED_PATH/g" "$TARGET_DIR/$APP_NAME.json"
else
sed -i -e "s/%%replace%%/$ESCAPED_PATH/g" "$TARGET_DIR/$APP_NAME.json"
fi
# Set permissions for the manifest so that all users can read it.
chmod o+r "$TARGET_DIR/$APP_NAME.json"
echo "Native messaging host for $BROWSER_NAME has been installed to $TARGET_DIR."