2014-02-01 11:59:39 +00:00
/ *
* Copyright ( c ) 2013 , Stefan Brand < seiichiro @ seiichiro0185 . org >
* All rights reserved .
*
* 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
* 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
* materials provided with the distribution .
*
* 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 ,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* /
import QtQuick 2.0
import Sailfish . Silica 1.0
import harbour . sailotp . FileIO 1.0 // Import FileIO Class
import "../lib/storage.js" as DB // Import the storage library for Config-Access
2014-02-01 16:06:34 +00:00
import "../lib/gibberish-aes.js" as Gibberish //Import AES encryption library
2014-02-01 11:59:39 +00:00
// Define Layout of the Export / Import Page
Dialog {
id: exportPage
2014-02-01 14:38:09 +00:00
// We get the Object of the parent page on call to refresh it after adding a new Entry
property QtObject parentPage: null
2014-02-01 16:06:34 +00:00
property string mode: "import"
2014-02-01 11:59:39 +00:00
2014-02-02 13:14:59 +00:00
function fillNum ( num ) {
if ( num < 10 ) {
return ( "0" + num ) ;
} else {
return ( num )
}
}
function creFileName ( ) {
var date = new Date ( ) ;
2014-02-02 13:28:19 +00:00
return ( XDG_HOME_DIR + "/sailotp_" + date . getFullYear ( ) + fillNum ( date . getMonth ( ) + 1 ) + fillNum ( date . getDate ( ) ) + ".aes" ) ;
2014-02-02 13:14:59 +00:00
}
function checkFileName ( file ) {
if ( mode == "export" ) {
if ( exportFile . exists ( file ) && ! fileOverwrite . checked ) {
2014-02-08 16:08:10 +00:00
notify . show ( qsTr ( "File already exists, choose \"Overwrite existing\" to overwrite it." ) , 4000 ) ;
2014-02-02 13:14:59 +00:00
return ( false )
} else {
return ( true )
}
} else {
if ( exportFile . exists ( file ) ) {
return ( true )
} else {
2014-02-08 16:08:10 +00:00
notify . show ( qsTr ( "Given file does not exist!" ) , 4000 ) ;
2014-02-02 13:14:59 +00:00
return ( false )
}
}
}
2014-02-01 11:59:39 +00:00
// FileIO Object for reading / writing files
FileIO {
2014-02-01 14:38:09 +00:00
id: exportFile
source: fileName . text
2014-02-02 13:14:59 +00:00
onError: { console . log ( msg ) ; }
2014-02-01 11:59:39 +00:00
}
SilicaFlickable {
id: exportFlickable
anchors.fill: parent
VerticalScrollDecorator { }
Column {
anchors.fill: parent
DialogHeader {
2014-02-08 16:08:10 +00:00
acceptText: mode == "export" ? qsTr ( "Export" ) : qsTr ( "Import" )
2014-02-01 11:59:39 +00:00
}
TextField {
id: fileName
width: parent . width
2014-02-02 13:28:19 +00:00
text: mode == "export" ? creFileName ( ) : XDG_HOME_DIR + "/" ;
2014-02-08 16:08:10 +00:00
label: qsTr ( "Filename" )
placeholderText: mode == "import" ? qsTr ( "File to import" ) : qsTr ( "File to export" )
2014-02-01 11:59:39 +00:00
focus: true
horizontalAlignment: TextInput . AlignLeft
2014-02-08 17:05:23 +00:00
EnterKey.enabled: text . length > 0
EnterKey.iconSource: "image://theme/icon-m-enter-next"
EnterKey.onClicked: filePassword . focus = true
2014-02-01 11:59:39 +00:00
}
2014-02-02 13:14:59 +00:00
TextSwitch {
id: fileOverwrite
checked: false
visible: mode == "export"
2014-02-08 16:08:10 +00:00
text: qsTr ( "Overwrite existing" )
2014-02-02 13:14:59 +00:00
}
2014-02-01 11:59:39 +00:00
TextField {
id: filePassword
width: parent . width
2014-02-08 16:08:10 +00:00
label: qsTr ( "Password" )
placeholderText: qsTr ( "Password for the file" )
2014-02-01 11:59:39 +00:00
echoMode: TextInput . Password
focus: true
horizontalAlignment: TextInput . AlignLeft
2014-02-08 17:05:23 +00:00
EnterKey.enabled: text . length > 0
EnterKey.iconSource: mode == "export" ? "image://theme/icon-m-enter-next" : "image://theme/icon-m-enter-accept"
EnterKey.onClicked: mode == "export" ? filePasswordCheck . focus = true : exportPage . accept ( )
2014-02-01 11:59:39 +00:00
}
2014-02-02 13:14:59 +00:00
TextField {
id: filePasswordCheck
width: parent . width
2014-02-08 16:08:10 +00:00
label: ( filePassword . text != filePasswordCheck . text && filePassword . text . length > 0 ) ? qsTr ( "Passwords don't match!" ) : qsTr ( "Passwords match!" )
placeholderText: qsTr ( "Repeated Password for the file" )
2014-02-02 13:14:59 +00:00
visible: mode == "export"
echoMode: TextInput . Password
focus: true
horizontalAlignment: TextInput . AlignLeft
2014-02-08 17:05:23 +00:00
EnterKey.enabled: filePassword . text == filePasswordCheck . text && filePassword . text . length > 0
EnterKey.iconSource: "image://theme/icon-m-enter-accept"
EnterKey.onClicked: exportPage . accept ( )
2014-02-02 13:14:59 +00:00
}
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"
2014-02-08 16:08:10 +00:00
text: qsTr ( "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." )
2014-02-02 13:14:59 +00:00
}
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"
2014-02-08 16:08:10 +00:00
text: qsTr ( "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." )
2014-02-02 13:14:59 +00:00
}
2014-02-01 11:59:39 +00:00
}
}
// Check if we can continue
2014-02-02 13:14:59 +00:00
canAccept: fileName . text . length > 0 && filePassword . text . length > 0 && ( mode == "import" || filePassword . text == filePasswordCheck . text ) && checkFileName ( fileName . text ) ? true : false
2014-02-01 11:59:39 +00:00
// Do the DB-Export / Import
onDone: {
if ( result == DialogResult . Accepted ) {
2014-02-02 13:14:59 +00:00
var plainText = ""
var chipherText = ""
2014-02-01 14:38:09 +00:00
if ( mode == "export" ) {
2014-02-02 13:14:59 +00:00
// Export Database to File
plainText = DB . db2json ( ) ;
if ( plainText != "" ) {
try {
chipherText = Gibberish . AES . enc ( plainText , filePassword . text ) ;
if ( ! exportFile . write ( chipherText ) ) {
2014-02-08 16:08:10 +00:00
notify . show ( qsTr ( "Error writing to file " ) + fileName . text , 4000 ) ;
2014-02-02 13:14:59 +00:00
} else {
2014-02-08 16:08:10 +00:00
notify . show ( qsTr ( "Token Database exported to " ) + fileName . text , 4000 ) ;
2014-02-02 13:14:59 +00:00
}
} catch ( e ) {
2014-02-08 16:08:10 +00:00
notify . show ( qsTr ( "Could not encrypt tokens. Error: " ) , 4000 ) ;
2014-02-02 13:14:59 +00:00
}
} else {
2014-02-08 16:08:10 +00:00
notify . show ( qsTr ( "Could not read tokens from Database" ) , 4000 ) ;
2014-02-02 13:14:59 +00:00
}
2014-02-01 14:38:09 +00:00
} else if ( mode == "import" ) {
2014-02-02 13:14:59 +00:00
// Import Tokens from File
chipherText = exportFile . read ( ) ;
if ( chipherText != "" ) {
try {
var errormsg = ""
plainText = Gibberish . AES . dec ( chipherText , filePassword . text ) ;
if ( DB . json2db ( plainText , errormsg ) ) {
2014-02-08 16:08:10 +00:00
notify . show ( qsTr ( "Tokens imported from " ) + fileName . text , 4000 ) ;
2014-02-02 13:14:59 +00:00
} else {
notify . show ( errormsg , 4000 ) ;
}
} catch ( e ) {
2014-02-08 16:08:10 +00:00
notify . show ( qsTr ( "Unable to decrypt file, did you use the right password?" ) , 4000 ) ;
2014-02-02 13:14:59 +00:00
}
} else {
2014-02-08 16:08:10 +00:00
notify . show ( qsTr ( "Could not read from file " ) + fileName . text , 4000 ) ;
2014-02-02 13:14:59 +00:00
}
2014-02-01 14:38:09 +00:00
}
2014-02-01 11:59:39 +00:00
}
}
}