1
0
Fork 0
mirror of https://github.com/seiichiro0185/sailotp.git synced 2024-11-22 07:39:42 +00:00

Error handling for export / import

Added error-handling for export / import
Added sanity-checks to export / import
This commit is contained in:
seiichiro 2014-02-02 14:14:59 +01:00
parent 9fe40ff767
commit e68cf226a4
6 changed files with 155 additions and 23 deletions

View file

@ -63,7 +63,6 @@ MouseArea {
id: notifyText id: notifyText
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.margins: Theme.paddingLarge anchors.margins: Theme.paddingLarge

View file

@ -97,11 +97,12 @@ function db2json() {
} }
// Read Values from JSON and put them into the DB // Read Values from JSON and put them into the DB
function json2db(jsonString) { function json2db(jsonString, error) {
var json = JSON.parse(jsonString); var json = JSON.parse(jsonString);
error = "";
if (json.version != "1" && json.app != "sailotp" ) { if (json.version != "1" && json.app != "sailotp" ) {
console.log("Unrecognized JSON format"); error = "Unrecognized format, file is not a SailOTP export";
return(false); return(false);
} else { } else {
var otpList = []; var otpList = [];
@ -114,8 +115,9 @@ function json2db(jsonString) {
} }
} }
parentPage.refreshOTPList(); parentPage.refreshOTPList();
return(true);
} else { } else {
console.log("File contains no Items"); error = "File contains no Tokens";
return(false); return(false);
} }
} }

View file

@ -40,14 +40,44 @@ Dialog {
// We get the Object of the parent page on call to refresh it after adding a new Entry // We get the Object of the parent page on call to refresh it after adding a new Entry
property QtObject parentPage: null property QtObject parentPage: null
property string mode: "import" property string mode: "import"
function fillNum(num) {
if (num < 10) {
return("0"+num);
} else {
return(num)
}
}
function creFileName() {
var date = new Date();
return("/home/nemo/sailotp_"+date.getFullYear()+fillNum(date.getMonth()+1)+fillNum(date.getDate())+".aes");
}
function checkFileName(file) {
if (mode == "export") {
if (exportFile.exists(file) && !fileOverwrite.checked) {
notify.show("File already exists, choose \"Overwrite existing\" to overwrite it.", 4000);
return(false)
} else {
return(true)
}
} else {
if (exportFile.exists(file)) {
return(true)
} else {
notify.show("Given file does not exist!", 4000);
return(false)
}
}
}
// FileIO Object for reading / writing files // FileIO Object for reading / writing files
FileIO { FileIO {
id: exportFile id: exportFile
source: fileName.text source: fileName.text
onError: console.log(msg) onError: { console.log(msg); }
} }
SilicaFlickable { SilicaFlickable {
@ -62,47 +92,124 @@ Dialog {
acceptText: mode == "export" ? "Export" : "Import" acceptText: mode == "export" ? "Export" : "Import"
} }
/*ComboBox {
id: modeSel
label: "Mode: "
menu: ContextMenu {
MenuItem { text: "Export"; onClicked: { mode = "export" } }
MenuItem { text: "Import"; onClicked: { mode = "import" } }
}
}*/
TextField { TextField {
id: fileName id: fileName
width: parent.width width: parent.width
text: mode == "export" ? creFileName() : "/home/nemo/";
label: "Filename" label: "Filename"
placeholderText: "File to Export / Import" placeholderText: mode == "import" ? "File to import" : "File to export"
focus: true focus: true
horizontalAlignment: TextInput.AlignLeft horizontalAlignment: TextInput.AlignLeft
} }
TextSwitch {
id: fileOverwrite
checked: false
visible: mode == "export"
text: "Overwrite existing"
}
TextField { TextField {
id: filePassword id: filePassword
width: parent.width width: parent.width
label: "Password" label: "Password"
placeholderText: "Password for the Export" placeholderText: "Password for the file"
echoMode: TextInput.Password echoMode: TextInput.Password
focus: true focus: true
horizontalAlignment: TextInput.AlignLeft horizontalAlignment: TextInput.AlignLeft
} }
TextField {
id: filePasswordCheck
width: parent.width
label: (filePassword.text != filePasswordCheck.text && filePassword.text.length > 0) ? "Passwords don't match!" : "Passwords match!"
placeholderText: "Repeated Password for the file"
visible: mode == "export"
echoMode: TextInput.Password
focus: true
horizontalAlignment: TextInput.AlignLeft
}
Text {
id: importText
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottomMargin: 20
width: parent.width - 2*Theme.paddingLarge
wrapMode: Text.Wrap
maximumLineCount: 15
font.pixelSize: Theme.fontSizeSmall
color: Theme.secondaryColor
visible: mode == "import"
text: "Here you can Import Tokens from a file. Put in the file location and the password you used on export. Pull left to start the import."
}
Text {
id: exportText
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottomMargin: 20
width: parent.width - 2*Theme.paddingLarge
wrapMode: Text.Wrap
maximumLineCount: 15
font.pixelSize: Theme.fontSizeSmall
color: Theme.secondaryColor
visible: mode == "export"
text: "Here you can export Tokens to a file. The exported file will be encrypted with AES-256-CBC and Base64 encoded. Choose a strong password, the file will contain the secrets used to generate the Tokens for your accounts. Pull left to start the export."
}
} }
} }
// Check if we can continue // Check if we can continue
canAccept: fileName.text.length > 0 && filePassword.text.length > 0 ? true : false canAccept: fileName.text.length > 0 && filePassword.text.length > 0 && (mode == "import" || filePassword.text == filePasswordCheck.text) && checkFileName(fileName.text) ? true : false
// Do the DB-Export / Import // Do the DB-Export / Import
// TODO: Error handling and enctyption
onDone: { onDone: {
if (result == DialogResult.Accepted) { if (result == DialogResult.Accepted) {
var plainText = ""
var chipherText = ""
if (mode == "export") { if (mode == "export") {
console.log("Exporting to " + fileName.text); // Export Database to File
exportFile.write(Gibberish.AES.enc(DB.db2json(), filePassword.text)); plainText = DB.db2json();
if (plainText != "") {
try {
chipherText = Gibberish.AES.enc(plainText, filePassword.text);
if (!exportFile.write(chipherText)) {
notify.show("Error writing to file "+ fileName.text, 4000);
} else {
notify.show("Token Database exported to "+ fileName.text, 4000);
}
} catch(e) {
notify.show("Could not encrypt tokens. Error: ", 4000);
}
} else {
notify.show("Could not read tokens from Database", 4000);
}
} else if(mode == "import") { } else if(mode == "import") {
console.log("Importing ftom " + fileName.text); // Import Tokens from File
DB.json2db(Gibberish.AES.dec(exportFile.read(), filePassword.text))
chipherText = exportFile.read();
if (chipherText != "") {
try {
var errormsg = ""
plainText = Gibberish.AES.dec(chipherText, filePassword.text);
if (DB.json2db(plainText, errormsg)) {
notify.show("Tokens imported from "+ fileName.text, 4000);
} else {
notify.show(errormsg, 4000);
}
} catch (e) {
notify.show("Unable to decrypt file, did you use the right password?", 4000);
}
} else {
notify.show("Could not read from file " + fileName.text, 4000);
}
} }
} }
} }

View file

@ -163,7 +163,7 @@ Page {
onClicked: { onClicked: {
Clipboard.text = otp Clipboard.text = otp
notify.show("Token for " + title + " copied", 3000); notify.show("Token for " + title + " copied to clipboard", 3000);
} }
ListView.onRemove: animateRemoval() ListView.onRemove: animateRemoval()

View file

@ -49,3 +49,25 @@ bool FileIO::write(const QString& data)
return true; return true;
} }
bool FileIO::exists()
{
if (mSource.isEmpty()) {
emit error("Source is empty!");
return false;
}
QFile file(mSource);
return file.exists();
}
bool FileIO::exists(const QString& filename)
{
if (filename.isEmpty()) {
emit error("Source is empty!");
return false;
}
QFile file(filename);
return file.exists();
}

View file

@ -16,6 +16,8 @@ public:
Q_INVOKABLE QString read(); Q_INVOKABLE QString read();
Q_INVOKABLE bool write(const QString& data); Q_INVOKABLE bool write(const QString& data);
Q_INVOKABLE bool exists();
Q_INVOKABLE bool exists(const QString& filename);
QString source() { return mSource; }; QString source() { return mSource; };