From 743b06f06555d70406115763406da4e825d39ba7 Mon Sep 17 00:00:00 2001 From: Stefan Brand Date: Sat, 1 Feb 2014 15:38:09 +0100 Subject: [PATCH] Added basic import and export The DB can be exported to json file Added handling of duplicate entries --- qml/lib/storage.js | 131 +++++++++++++++++++++++++++++---------- qml/pages/ExportPage.qml | 18 ++++-- qml/pages/MainView.qml | 42 ++++++------- 3 files changed, 133 insertions(+), 58 deletions(-) diff --git a/qml/lib/storage.js b/qml/lib/storage.js index f555470..9157088 100644 --- a/qml/lib/storage.js +++ b/qml/lib/storage.js @@ -5,25 +5,25 @@ * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * - * 1. Redistributions of source code must retain the above copyright notice, this + * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this - * list of conditions and the following disclaimer in the documentation and/or other + * list of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. - * - * 3. The names of the contributors may not be used to endorse or promote products + * + * 3. The names of the contributors may not be used to endorse or promote products * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -39,8 +39,7 @@ function getDB() { db.changeVersion("", "2", function(tx) { tx.executeSql("CREATE TABLE IF NOT EXISTS OTPStorage(title TEXT, secret TEXT, type TEXT DEFAULT 'TOPT', counter INTEGER DEFAULT 0, fav INTEGER DEFAULT 0);"); - } - ); + }); } else if (db.version == "1.0") { // Upgrade DB Schema to Version 2 db.changeVersion("1.0", "2", @@ -48,8 +47,7 @@ function getDB() { tx.executeSql("ALTER TABLE OTPStorage ADD COLUMN type TEXT DEFAULT 'TOTP';"); tx.executeSql("ALTER TABLE OTPStorage ADD COLUMN counter INTEGER DEFAULT 0;"); tx.executeSql("ALTER TABLE OTPStorage ADD COLUMN fav INTEGER DEFAULT 0;"); - } - ); + }); } } catch (e) { // DB Failed to open @@ -70,8 +68,57 @@ function getOTP() { mainPage.appendOTP(res.rows.item(i).title, res.rows.item(i).secret, res.rows.item(i).type, res.rows.item(i).counter, res.rows.item(i).fav); if (res.rows.item(i).fav) mainPage.setCoverOTP(res.rows.item(i).title, res.rows.item(i).secret, res.rows.item(i).type); } + }); +} + +// Get all OTP Values and put them into a JSON-Object +function db2json(password) { + var db = getDB(); + var otpList = []; + + db.transaction( + function(tx) { + var res = tx.executeSql("select * from OTPStorage;"); + for (var i=0; i < res.rows.length; i++) { + otpList.push({ + "title": res.rows.item(i).title, + "secret": res.rows.item(i).secret, + "type": res.rows.item(i).type, + "counter": res.rows.item(i).counter, + }); } - ) + }); + + if (otpList.length > 0) { + return(JSON.stringify({"app": "sailotp", "version": 1, "otplist": otpList})); + } else { + return("") + } +} + +// Read Values from JSON and put them into the DB +function json2db(jsonString, password) { + var json = JSON.parse(jsonString); + + if (json.version != "1" && json.app != "sailotp" ) { + console.log("Unrecognized JSON format"); + return(false); + } else { + var otpList = []; + otpList = json.otplist; + if (otpList.length > 0) { + while(otpList.length > 0) { + var otpItem = otpList.shift(); + if (otpItem.title != "" & otpItem.secret.length >= 16) { + addOTP(otpItem.title, otpItem.secret, otpItem.type, otpItem.counter); + } + } + parentPage.refreshOTPList(); + } else { + console.log("File contains no Items"); + return(false); + } + } } // Add a new OTP @@ -80,9 +127,26 @@ function addOTP(title, secret, type, counter) { db.transaction( function(tx) { - tx.executeSql("INSERT INTO OTPStorage VALUES(?, ?, ?, ?, ?);", [title, secret, type, counter, 0]); - } - ) + if (checkOTP(title, secret)) { + console.log("Token " + title + " is already in DB"); + } else { + tx.executeSql("INSERT INTO OTPStorage VALUES(?, ?, ?, ?, ?);", [title, secret, type, counter, 0]); + console.log("Token " + title + " added."); + } + }); +} + +// Check if an OTP Token already exists in the DB +function checkOTP(title, secret) { + var db = getDB(); + var res + + db.transaction( + function(tx) { + res = tx.executeSql("select title FROM OTPStorage WHERE title=? and secret=?;", [title, secret]); + }); + + return res.rows.length > 0 ? true : false } // Remove an existing OTP @@ -92,10 +156,10 @@ function removeOTP(title, secret) { db.transaction( function(tx) { tx.executeSql("DELETE FROM OTPStorage WHERE title=? and secret=?;", [title, secret]); - } - ) + }); } +// Set OTP to favourite function setFav(title, secret) { var db = getDB(); @@ -103,18 +167,17 @@ function setFav(title, secret) { function(tx) { tx.executeSql("UPDATE OTPStorage set fav = 0"); tx.executeSql("UPDATE OTPStorage set fav = 1 WHERE title=? and secret=?;", [title, secret]); - } - ) + }); } +// Reset favourite Flag for OTP function resetFav(title, secret) { var db = getDB(); db.transaction( function(tx) { tx.executeSql("UPDATE OTPStorage set fav = 0"); - } - ) + }); } // Change an existing OTP @@ -123,12 +186,15 @@ function changeOTP(title, secret, type, counter, oldtitle, oldsecret) { db.transaction( function(tx) { - tx.executeSql("UPDATE OTPStorage SET title=?, secret=?, type=?, counter=? WHERE title=? and secret=?;", [title, secret, type, counter, oldtitle, oldsecret]); - } - ) + if (checkOTP(title, secret)) { + console.log("Token " + title + " is already in DB"); + } else { + tx.executeSql("UPDATE OTPStorage SET title=?, secret=?, type=?, counter=? WHERE title=? and secret=?;", [title, secret, type, counter, oldtitle, oldsecret]); + console.log("Token " + title + " modified."); + } + }); } - // Get the counter for a HOTP value, incerment the counter on request function getCounter(title, secret, increment) { var db = getDB(); @@ -139,8 +205,7 @@ function getCounter(title, secret, increment) { function(tx) { res = tx.executeSql("SELECT counter FROM OTPStorage where title=? and secret=?;", [title, secret]); if (increment) tx.executeSql("UPDATE OTPStorage set counter=counter+1 WHERE title=? and secret=?;", [title, secret]); - } - ) + }); return res.rows.item(0).counter; } diff --git a/qml/pages/ExportPage.qml b/qml/pages/ExportPage.qml index 1853f7e..bb30fc7 100644 --- a/qml/pages/ExportPage.qml +++ b/qml/pages/ExportPage.qml @@ -37,13 +37,16 @@ import "../lib/storage.js" as DB // Import the storage library for Config-Access Dialog { id: exportPage + // We get the Object of the parent page on call to refresh it after adding a new Entry + property QtObject parentPage: null + property string mode: "export" // FileIO Object for reading / writing files FileIO { - id: exportFile - source: fileName.text - onError: console.log(msg) + id: exportFile + source: fileName.text + onError: console.log(msg) } SilicaFlickable { @@ -90,9 +93,16 @@ Dialog { canAccept: fileName.text.length > 0 && filePassword.text.length > 0 ? true : false // Do the DB-Export / Import + // TODO: Error handling and enctyption onDone: { if (result == DialogResult.Accepted) { - // TODO + if (mode == "export") { + console.log("Exporting to " + fileName.text, filePassword.text); + exportFile.write(DB.db2json()); + } else if(mode == "import") { + console.log("Importing ftom " + fileName.text, filePassword.text); + DB.json2db(exportFile.read(), filePassword) + } } } } diff --git a/qml/pages/MainView.qml b/qml/pages/MainView.qml index 0880d22..fa7da47 100644 --- a/qml/pages/MainView.qml +++ b/qml/pages/MainView.qml @@ -5,25 +5,25 @@ * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * - * 1. Redistributions of source code must retain the above copyright notice, this + * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this - * list of conditions and the following disclaimer in the documentation and/or other + * list of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. - * - * 3. The names of the contributors may not be used to endorse or promote products + * + * 3. The names of the contributors may not be used to endorse or promote products * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -40,7 +40,7 @@ Page { id: otpListModel } - // This holds the time of the last update of the page as Unix Timestamp (in Milliseconds) + // This holds the time of the last update of the page as Unix Timestamp (in Milliseconds) property double lastUpdated: 0 // Add an entry to the list @@ -71,11 +71,11 @@ Page { // Calculate new OTPs for every entry function refreshOTPValues() { - // get seconds from current Date - var curDate = new Date(); + // get seconds from current Date + var curDate = new Date(); var seconds = curDate.getSeconds(); - // Iterate over all List entries + // Iterate over all List entries for (var i=0; i