/* Copyright (C) 2012 Richard Mitchell * * 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. */ /*jslint unparam: true, white: true, browser: true */ /*global FileReader: true, File: true, alert: true, jQuery: true */ (function ($) { "use strict"; var KeePass = window.KeePass, PERCENT_RE = /\b(\d{1,3})%/, droppedFiles = []; $(function () { var opening = false, passwordToggle = '<a href="#" class="password-toggle hidden">Show password</a>'; function readFiles(files, callback) { var reader = new FileReader(), fileArray = [], i; if (files.length !== 1) { return false; } for (i = 0; i < files.length; i += 1) { fileArray.push(files[i]); } function doRead(fileArray) { $(reader).bind('loadend', function (ev) { var file = { name: fileArray[0].name, size: fileArray[0].size, type: fileArray[0].type, data: reader.result }; callback(file); fileArray.shift(); if (fileArray.length) { doRead(fileArray); } }); reader.readAsBinaryString(fileArray[0]); } doRead(fileArray); } function pwMask(len) { /* Could also be done by (new Array(len)).join('*');, but lint no likey. */ var result = '', i; for (i = 0; i < len; i += 1) { result += '*'; } return result; } function createEntry(entry) { var binary = ( entry.binaryData ? '<a href="data:application/octet-stream;base64,' + encodeURIComponent((window.btoa || base64.encode)(entry.binaryData)) + '"' + (entry.binaryDescription ? ' download="' + encodeURIComponent(entry.binaryDescription) + '"' : '') + '>' + (entry.binaryDescription || 'Attached file') + '</a>' : '' ), entryMarkup = $('<tr><td class="title"><a href="' + entry.url + '">' + entry.title + '</a></td><td class="userName">' + entry.userName + '</td><td class="password"><span>' + pwMask(entry.password.length) + '</span>' + passwordToggle + '</td><td class="notes"><pre>' + entry.additional + '</pre></td><td>' + binary + '</td></tr>'); entryMarkup.find('.password-toggle').click(function (ev) { ev.preventDefault(); var $this = $(this), $field = $this.parent(); if ($this.hasClass('hidden')) { $this.text('Hide password'); $field.find('span').hide(); $field.prepend($('<input type="text" value="' + entry.password + '"/>')); } else { $this.text('Show password'); $field.find('span').show(); $field.find('input').remove(); } $this.toggleClass('hidden'); }); return entryMarkup; } function doNotDisplay(entry) { return (entry.title==='Meta-Info' && entry.userName==='SYSTEM' && entry.url==='$' && entry.additional==="KPX_GROUP_TREE_STATE"); } function createSubGroups(groups, container) { var g, e = null, l = $('<ul class="groups"></ul>'), groupMarkup, entriesContainer, entryMarkup, entry; container.append(l); for (g = 0; g < groups.length; g += 1) { groupMarkup = $('<li><a class="group-name" href="#">' + groups[g].name + '</a></li>'); entriesContainer = $('<table class="entries"><thead><tr><th class="title">Title</th><th class="userName">Username</th><th class="password">Password</th><th class="notes">Notes</th><th class="attachment">Attachment</th></tr></thead></table>'); for (e in groups[g].entries) { if (groups[g].entries.hasOwnProperty(e) && !doNotDisplay(groups[g].entries[e])) { entry = groups[g].entries[e]; entryMarkup = createEntry(entry); entriesContainer.append(entryMarkup); } } groupMarkup.append(entriesContainer); l.append(groupMarkup); createSubGroups(groups[g].subGroups, groupMarkup); } } function opened(e) { var container = $('#db-contents'), manager = e.manager, groups = manager.database.subGroups; $('#open').removeAttr('disabled'); $('#spinner').hide(); opening = false; createSubGroups(groups, container); $('#keepassopenform').hide(); $('#db-contents-wrapper').show(); } document.addEventListener('keePassDatabaseOpen', opened); function openFailed(e) { var manager = e.manager, message = e.exception, $errors = $('#errors'); $('#spinner').hide(); opening = false; manager.status(null); $('#open').removeAttr('disabled'); $errors.text(message); $errors.slideDown(); } document.addEventListener('keePassDatabaseOpenError', openFailed); function statusCallback(msg) { var $status = $('#status'), $message = $status.find('p'), $progress = $status.find('progress'), $spinner = $status.find('img'), percentage; if (msg===null) { $message.hide(); $progress.hide(); $spinner.hide(); } else { percentage = PERCENT_RE.test(msg) ? PERCENT_RE.exec(msg)[1] : null; if (percentage !== null) { $progress.attr('value', percentage); $progress.text(percentage + '%'); $spinner.hide(); $progress.show(); } else { $progress.hide(); $spinner.show(); } $message.text(msg); $message.show(); } } function process() { var key = $('#password').val(), diskDrive = !!$('#use_keyfile:checked').length, providerName = 'KeePassJS', manager = new KeePass.Manager(statusCallback); if (opening) { return; } opening = true; $('#open').attr('disabled', 'disabled'); function loadWithKeyFile(keyFile) { readFiles($('#keepassfile').get(0).files, function (file) { manager.setMasterKey( key, diskDrive, keyFile, providerName); manager.open(file.data); }); } if (diskDrive) { readFiles($('#keyfile').get(0).files, function (keyfile) { loadWithKeyFile(keyfile); }); } else { loadWithKeyFile(null); } } $('#open').bind('click', function (ev) { ev.preventDefault(); process(); }); $('#keepassopenform').bind('submit', function (ev) { ev.preventDefault(); process(); }); $('#db-contents').on('click', 'a.group-name', function (ev) { ev.preventDefault(); $('#db-contents table').hide(); $(this).parent().children('table').show(); }); $('#close').bind('click', function (ev) { ev.preventDefault(); $('#db-contents').text(''); $('#db-contents-wrapper').hide(); $('#keepassopenform').show(); $("#keepassopenform input:not([type='button'])").each(function() {$(this).val('');}); $("#keepassopenform input:first").focus(); }); }); }(jQuery));