2014-02-02 13:43:22 +00:00
/ *
2014-07-01 14:37:52 +00:00
* Copyright ( c ) 2014 , Stefan Brand < seiichiro @ seiichiro0185 . org >
2014-02-02 13:43:22 +00:00
* 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 .
* /
2023-02-26 15:40:10 +00:00
import QtQuick 2.2
2014-02-02 13:43:22 +00:00
import Sailfish . Silica 1.0
2023-02-26 15:40:10 +00:00
import Sailfish . Pickers 1.0
2014-02-02 13:43:22 +00:00
import harbour . sailotp . FileIO 1.0 // Import FileIO Class
2023-02-26 15:40:10 +00:00
import harbour . sailotp . QCipher 1.0 // Import FileIO Class
2014-02-02 13:43:22 +00:00
import "../lib/storage.js" as DB // Import the storage library for Config-Access
// Define Layout of the Export / Import Page
Dialog {
id: exportPage
2015-12-06 18:37:41 +00:00
allowedOrientations: Orientation . All
2014-02-02 13:43:22 +00:00
// 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: "import"
function fillNum ( num ) {
if ( num < 10 ) {
return ( "0" + num ) ;
} else {
return ( num )
}
}
function creFileName ( ) {
var date = new Date ( ) ;
return ( XDG_HOME_DIR + "/sailotp_" + date . getFullYear ( ) + fillNum ( date . getMonth ( ) + 1 ) + fillNum ( date . getDate ( ) ) + ".aes" ) ;
}
function checkFileName ( file ) {
if ( mode == "export" ) {
if ( exportFile . exists ( file ) && ! fileOverwrite . checked ) {
2014-02-09 09:59:50 +00:00
notify . show ( qsTr ( "File already exists, choose \"Overwrite existing\" to overwrite it." ) , 4000 ) ;
2014-02-02 13:43:22 +00:00
return ( false )
} else {
return ( true )
}
} else {
if ( exportFile . exists ( file ) ) {
return ( true )
} else {
2014-02-09 09:59:50 +00:00
notify . show ( qsTr ( "Given file does not exist!" ) , 4000 ) ;
2014-02-02 13:43:22 +00:00
return ( false )
}
}
}
// FileIO Object for reading / writing files
FileIO {
id: exportFile
source: fileName . text
onError: { console . log ( msg ) ; }
}
2023-02-26 15:40:10 +00:00
// QCipher Object for Encryption
QCipher {
id: cipher
}
2014-02-02 13:43:22 +00:00
SilicaFlickable {
id: exportFlickable
anchors.fill: parent
2014-07-01 14:37:52 +00:00
PullDownMenu {
MenuItem {
text: mode == "export" ? qsTr ( "Import" ) : qsTr ( "Export" )
onClicked: {
if ( mode == "export" ) {
mode = "import"
2023-02-26 15:40:10 +00:00
fileName . text = ""
2014-07-01 14:37:52 +00:00
} else {
mode = "export"
2023-02-26 15:40:10 +00:00
fileName . text = creFileName ( )
2014-07-01 14:37:52 +00:00
}
}
}
}
2023-02-26 15:40:10 +00:00
// FilePicker for the Input File
Component {
id: filePickerPage
FilePickerPage {
nameFilters: [ '*' ]
onSelectedContentPropertiesChanged: {
fileName . text = selectedContentProperties . filePath
}
}
}
2014-02-02 13:43:22 +00:00
VerticalScrollDecorator { }
2023-02-26 15:40:10 +00:00
Column {
2014-02-02 13:43:22 +00:00
anchors.fill: parent
DialogHeader {
2014-02-09 09:59:50 +00:00
acceptText: mode == "export" ? qsTr ( "Export" ) : qsTr ( "Import" )
2014-02-02 13:43:22 +00:00
}
TextField {
id: fileName
width: parent . width
2023-02-26 15:40:10 +00:00
text: mode == "export" ? creFileName ( ) : "" ;
2014-02-09 09:59:50 +00:00
label: qsTr ( "Filename" )
2023-02-26 15:40:10 +00:00
placeholderText: qsTr ( "File to export" )
visible: mode == "export"
2014-02-02 13:43:22 +00:00
focus: true
horizontalAlignment: TextInput . AlignLeft
2014-02-09 09:59:50 +00:00
EnterKey.enabled: text . length > 0
EnterKey.iconSource: "image://theme/icon-m-enter-next"
EnterKey.onClicked: filePassword . focus = true
2014-02-02 13:43:22 +00:00
}
2023-02-26 15:40:10 +00:00
ValueButton {
width: parent . width
label: qsTr ( "File to import" )
value: fileName . text ? fileName.text : "None"
visible: mode == "import"
onClicked: pageStack . push ( filePickerPage )
}
2014-02-02 13:43:22 +00:00
TextSwitch {
id: fileOverwrite
checked: false
visible: mode == "export"
2014-02-09 09:59:50 +00:00
text: qsTr ( "Overwrite existing" )
2014-02-02 13:43:22 +00:00
}
TextField {
id: filePassword
width: parent . width
2014-02-09 09:59:50 +00:00
label: qsTr ( "Password" )
placeholderText: qsTr ( "Password for the file" )
2014-02-02 13:43:22 +00:00
echoMode: TextInput . Password
focus: true
horizontalAlignment: TextInput . AlignLeft
2014-02-09 09:59:50 +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-02 13:43:22 +00:00
}
TextField {
id: filePasswordCheck
width: parent . width
2014-02-09 09:59:50 +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:43:22 +00:00
visible: mode == "export"
echoMode: TextInput . Password
focus: true
horizontalAlignment: TextInput . AlignLeft
2014-02-09 09:59:50 +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:43:22 +00:00
}
Text {
id: importText
anchors.horizontalCenter: parent . horizontalCenter
anchors.bottomMargin: 20
width: parent . width - 2 * Theme . paddingLarge
wrapMode: Text . Wrap
maximumLineCount: 15
2018-04-02 07:32:11 +00:00
font.pixelSize: Theme . fontSizeTiny
2014-02-02 13:43:22 +00:00
color: Theme . secondaryColor
visible: mode == "import"
2014-02-09 09:59:50 +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:43:22 +00:00
}
Text {
id: exportText
anchors.horizontalCenter: parent . horizontalCenter
anchors.bottomMargin: 20
width: parent . width - 2 * Theme . paddingLarge
wrapMode: Text . Wrap
maximumLineCount: 15
2018-04-02 07:32:11 +00:00
font.pixelSize: Theme . fontSizeTiny
2014-02-02 13:43:22 +00:00
color: Theme . secondaryColor
visible: mode == "export"
2023-02-26 15:40: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." ) + "\n\n" + qsTr ( "To view the content of the export file outside of SailOTP use the following openssl command:" ) + "\n\nopenssl enc -d -a -aes-256-cbc -in <file>"
2014-02-02 13:43:22 +00:00
}
}
}
// Check if we can continue
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
onDone: {
if ( result == DialogResult . Accepted ) {
var plainText = ""
var chipherText = ""
if ( mode == "export" ) {
// Export Database to File
plainText = DB . db2json ( ) ;
if ( plainText != "" ) {
try {
2023-02-26 15:40:10 +00:00
chipherText = cipher . encrypt ( plainText , filePassword . text ) ;
if ( chipherText != "" ) {
if ( ! exportFile . write ( chipherText ) ) {
notify . show ( qsTr ( "Error writing to file " ) + fileName . text , 4000 ) ;
} else {
notify . show ( qsTr ( "Token Database exported to " ) + fileName . text , 4000 ) ;
}
2014-02-02 13:43:22 +00:00
} else {
2023-02-26 15:40:10 +00:00
notify . show ( qsTr ( "Could not encrypt tokens. Error: " ) , 4000 ) ;
2014-02-02 13:43:22 +00:00
}
} catch ( e ) {
2023-02-26 15:40:10 +00:00
notify . show ( qsTr ( "Could not encrypt tokens. Error: " ) + e , 4000 ) ;
2014-02-02 13:43:22 +00:00
}
} else {
2014-02-09 09:59:50 +00:00
notify . show ( qsTr ( "Could not read tokens from Database" ) , 4000 ) ;
2014-02-02 13:43:22 +00:00
}
} else if ( mode == "import" ) {
// Import Tokens from File
chipherText = exportFile . read ( ) ;
if ( chipherText != "" ) {
try {
var errormsg = ""
2023-02-26 15:40:10 +00:00
plainText = cipher . decrypt ( chipherText , filePassword . text )
2014-02-02 13:43:22 +00:00
if ( DB . json2db ( plainText , errormsg ) ) {
2014-02-09 09:59:50 +00:00
notify . show ( qsTr ( "Tokens imported from " ) + fileName . text , 4000 ) ;
2014-02-02 13:43:22 +00:00
} else {
notify . show ( errormsg , 4000 ) ;
}
} catch ( e ) {
2014-02-09 09:59:50 +00:00
notify . show ( qsTr ( "Unable to decrypt file, did you use the right password?" ) , 4000 ) ;
2014-02-02 13:43:22 +00:00
}
} else {
2014-02-09 09:59:50 +00:00
notify . show ( qsTr ( "Could not read from file " ) + fileName . text , 4000 ) ;
2014-02-02 13:43:22 +00:00
}
}
}
}
}