// Copyright (c) 2020 Proton Technologies AG // // This file is part of ProtonMail Bridge. // // ProtonMail Bridge is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ProtonMail Bridge is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with ProtonMail Bridge. If not, see . // This is main qml file import QtQuick 2.8 import ImportExportUI 1.0 import ProtonUI 1.0 // All imports from dynamic must be loaded before import QtQuick.Window 2.2 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 Item { id: gui property alias winMain: winMain property bool isFirstWindow: true property int warningFlags: 0 property var locale : Qt.locale("en_US") property date netBday : new Date("1989-03-13T00:00:00") property var allYears : getYearList(1970,(new Date()).getFullYear()) property var allMonths : getMonthList(1,12) property var allDays : getDayList(1,31) property var enums : JSON.parse('{"pathOK":1,"pathEmptyPath":2,"pathWrongPath":4,"pathNotADir":8,"pathWrongPermissions":16,"pathDirEmpty":32,"errUnknownError":0,"errEventAPILogout":1,"errUpdateAPI":2,"errUpdateJSON":3,"errUserAuth":4,"errQApplication":18,"errEmailExportFailed":6,"errEmailExportMissing":7,"errNothingToImport":8,"errEmailImportFailed":12,"errDraftImportFailed":13,"errDraftLabelFailed":14,"errEncryptMessageAttachment":15,"errEncryptMessage":16,"errNoInternetWhileImport":17,"errUnlockUser":5,"errSourceMessageNotSelected":19,"errCannotParseMail":5000,"errWrongLoginOrPassword":5001,"errWrongServerPathOrPort":5002,"errWrongAuthMethod":5003,"errIMAPFetchFailed":5004,"errLocalSourceLoadFailed":1000,"errPMLoadFailed":1001,"errRemoteSourceLoadFailed":1002,"errLoadAccountList":1005,"errExit":1006,"errRetry":1007,"errAsk":1008,"errImportFailed":1009,"errCreateLabelFailed":1010,"errCreateFolderFailed":1011,"errUpdateLabelFailed":1012,"errUpdateFolderFailed":1013,"errFillFolderName":1014,"errSelectFolderColor":1015,"errNoInternet":1016,"folderTypeSystem":"system","folderTypeLabel":"label","folderTypeFolder":"folder","folderTypeExternal":"external","progressInit":"init","progressLooping":"looping","statusNoInternet":"noInternet","statusCheckingInternet":"internetCheck","statusNewVersionAvailable":"oldVersion","statusUpToDate":"upToDate","statusForceUpdate":"forceupdate"}') IEStyle{} MainWindow { id: winMain visible : true Component.onCompleted: { winMain.showAndRise() } } BugReportWindow { id:bugreportWin clientVersion.visible: false onPrefill : { userAddress.text="" if (accountsModel.count>0) { var addressList = accountsModel.get(0).aliases.split(";") if (addressList.length>0) { userAddress.text = addressList[0] } } } } // Signals from Go Connections { target: go onProcessFinished : { winMain.dialogAddUser.hide() winMain.dialogGlobal.hide() } onOpenManual : Qt.openUrlExternally("https://protonmail.com/support/categories/import-export/") onNotifyBubble : { //go.highlightSystray() winMain.bubleNote.text = message winMain.bubleNote.place(tabIndex) winMain.bubleNote.show() winMain.showAndRise() } onBubbleClosed : { if (winMain.updateState=="uptodate") { //go.normalSystray() } } onSetConnectionStatus: { go.isConnectionOK = isAvailable if (go.isConnectionOK) { if( winMain.updateState==gui.enums.statusNoInternet) { go.setUpdateState(gui.enums.statusUpToDate) } } else { go.setUpdateState(gui.enums.statusNoInternet) } } onRunCheckVersion : { go.setUpdateState(gui.enums.statusUpToDate) winMain.dialogGlobal.state=gui.enums.statusCheckingInternet winMain.dialogGlobal.show() go.isNewVersionAvailable(showMessage) } onSetUpdateState : { // once app is outdated prevent from state change if (winMain.updateState != gui.enums.statusForceUpdate) { winMain.updateState = updateState } } onSetAddAccountWarning : winMain.dialogAddUser.setWarning(message, 0) onNotifyVersionIsTheLatest : { winMain.popupMessage.show( qsTr("You have the latest version!", "todo") ) } onNotifyError : { var sep = go.errorDescription.indexOf("\n") < 0 ? go.errorDescription.length : go.errorDescription.indexOf("\n") var name = go.errorDescription.slice(0, sep) var errorMessage = go.errorDescription.slice(sep) switch (errCode) { case gui.enums.errPMLoadFailed : winMain.popupMessage.show ( qsTr ( "Loading ProtonMail folders and labels was not successful." , "Error message" ) ) winMain.dialogExport.hide() break case gui.enums.errLocalSourceLoadFailed : winMain.popupMessage.show(qsTr( "Loading local folder structure was not successful. "+ "Folder does not contain valid MBOX or EML file.", "Error message when can not find correct files in folder." )) winMain.dialogImport.hide() break case gui.enums.errRemoteSourceLoadFailed : winMain.popupMessage.show ( qsTr ( "Loading remote source structure was not successful." , "Error message" ) ) winMain.dialogImport.hide() break case gui.enums.errWrongServerPathOrPort : winMain.popupMessage.show ( qsTr ( "Cannot contact server - incorrect server address and port." , "Error message" ) ) winMain.dialogImport.decrementCurrentIndex() break case gui.enums.errWrongLoginOrPassword : winMain.popupMessage.show ( qsTr ( "Cannot authenticate - Incorrect email or password." , "Error message" ) ) winMain.dialogImport.decrementCurrentIndex() break ; case gui.enums.errWrongAuthMethod : winMain.popupMessage.show ( qsTr ( "Cannot authenticate - Please use secured authentication method." , "Error message" ) ) winMain.dialogImport.decrementCurrentIndex() break ; case gui.enums.errFillFolderName: winMain.popupMessage.show(qsTr ( "Please fill the name.", "Error message when user did not fill the name of folder or label" )) break case gui.enums.errCreateLabelFailed: winMain.popupMessage.show(qsTr( "Cannot create label with name \"%1\"\n%2", "Error message when it is not possible to create new label, arg1 folder name, arg2 error reason" ).arg(name).arg(errorMessage)) break case gui.enums.errCreateFolderFailed: winMain.popupMessage.show(qsTr( "Cannot create folder with name \"%1\"\n%2", "Error message when it is not possible to create new folder, arg1 folder name, arg2 error reason" ).arg(name).arg(errorMessage)) break case gui.enums.errNothingToImport: winMain.popupMessage.show ( qsTr ( "No emails left to import after date range applied. Please, change the date range to continue." , "Error message" ) ) winMain.dialogImport.decrementCurrentIndex() break case gui.enums.errNoInternetWhileImport: case gui.enums.errNoInternet: go.setConnectionStatus(false) winMain.popupMessage.show ( go.canNotReachAPI ) break case gui.enums.errPMAPIMessageTooLarge: case gui.enums.errIMAPFetchFailed: case gui.enums.errEmailImportFailed : case gui.enums.errDraftImportFailed : case gui.enums.errDraftLabelFailed : case gui.enums.errEncryptMessageAttachment: case gui.enums.errEncryptMessage: //winMain.dialogImport.ask_retry_skip_cancel(name, errorMessage) console.log("Import error", errCode, go.errorDescription) winMain.popupMessage.show(qsTr("Error during import: \n%1\n please see log files for more details.", "message of generic error").arg(go.errorDescription)) winMain.dialogImport.hide() break; case gui.enums.errUnknownError : default: console.log("Unknown Error", errCode, go.errorDescription) winMain.popupMessage.show(qsTr("The program encounter an unknown error \n%1\n please see log files for more details.", "message of generic error").arg(go.errorDescription)) winMain.dialogExport.hide() winMain.dialogImport.hide() winMain.dialogAddUser.hide() winMain.dialogGlobal.hide() } } onNotifyUpdate : { go.setUpdateState("forceUpdate") if (!winMain.dialogUpdate.visible) { gui.openMainWindow(true) go.runCheckVersion(false) winMain.dialogUpdate.show() } } onNotifyLogout : { go.notifyBubble(0, qsTr("Account %1 has been disconnected. Please log in to continue to use the Import-Export app with this account.").arg(accname) ) } onNotifyAddressChanged : { go.notifyBubble(0, qsTr("The address list has been changed for account %1. You may need to reconfigure the settings in your email client.").arg(accname) ) } onNotifyAddressChangedLogout : { go.notifyBubble(0, qsTr("The address list has been changed for account %1. You have to reconfigure the settings in your email client.").arg(accname) ) } onNotifyKeychainRebuild : { go.notifyBubble(1, qsTr( "Your MacOS keychain is probably corrupted. Please consult the instructions in our FAQ.", "notification message" )) } onNotifyHasNoKeychain : { gui.winMain.dialogGlobal.state="noKeychain" gui.winMain.dialogGlobal.show() } onExportStructureLoadFinished: { if (okay) winMain.dialogExport.okay() else winMain.dialogExport.cancel() } onImportStructuresLoadFinished: { if (okay) winMain.dialogImport.okay() else winMain.dialogImport.cancel() } onSimpleErrorHappen: { if (winMain.dialogImport.visible == true) { winMain.dialogImport.hide() } if (winMain.dialogExport.visible == true) { winMain.dialogExport.hide() } } } function folderIcon(folderName, folderType) { // translations switch (folderName.toLowerCase()) { case "inbox" : return Style.fa.inbox case "sent" : return Style.fa.send case "spam" : case "junk" : return Style.fa.ban case "draft" : return Style.fa.file_o case "starred" : return Style.fa.star_o case "trash" : return Style.fa.trash_o case "archive" : return Style.fa.archive default: return folderType == gui.enums.folderTypeLabel ? Style.fa.tag : Style.fa.folder_open } return Style.fa.sticky_note_o } function folderTypeTitle(folderType) { // translations if (folderType==gui.enums.folderTypeSystem ) return "" if (folderType==gui.enums.folderTypeLabel ) return qsTr("Labels" , "todo") if (folderType==gui.enums.folderTypeFolder ) return qsTr("Folders" , "todo") return "Undef" } function isFolderEmpty() { return "true" } function getUnixTime(dateString) { var d = new Date(dateString) var n = d.getTime() if (n != n) return -1 return n } function getYearList(minY,maxY) { var years = new Array() for (var i=0; i<=maxY-minY;i++) { years[i] = (maxY-i).toString() } //console.log("getYearList:", years) return years } function getMonthList(minM,maxM) { var months = new Array() for (var i=0; i<=maxM-minM;i++) { var iMonth = new Date(1989,(i+minM-1),13) months[i] = iMonth.toLocaleString(gui.locale, "MMM") } //console.log("getMonthList:", months[0], months) return months } function getDayList(minD,maxD) { var days = new Array() for (var i=0; i<=maxD-minD;i++) { days[i] = gui.prependZeros(i+minD,2) } return days } function prependZeros(num,desiredLength) { var s = num+"" while (s.length < desiredLength) s="0"+s return s } function daysInMonth(year,month) { if (typeof(year) !== 'number') { year = parseInt(year) } if (typeof(month) !== 'number') { month = Date.fromLocaleDateString( gui.locale, "1970-"+month+"-10", "yyyy-MMM-dd").getMonth()+1 } var maxDays = (new Date(year,month,0)).getDate() if (isNaN(maxDays)) maxDays = 0 //console.log(" daysInMonth", year, month, maxDays) return maxDays } function niceDateTime() { var stamp = new Date() var nice = getMonthList(stamp.getMonth()+1, stamp.getMonth()+1)[0] nice += "-" + getDayList(stamp.getDate(), stamp.getDate())[0] nice += "-" + getYearList(stamp.getFullYear(), stamp.getFullYear())[0] nice += " " + gui.prependZeros(stamp.getHours(),2) nice += ":" + gui.prependZeros(stamp.getMinutes(),2) return nice } /* // Debug Connections { target: structureExternal onDataChanged: { console.log("external data changed") } } // Debug Connections { target: structurePM onSelectedLabelsChanged: console.log("PM sel labels:", structurePM.selectedLabels) onSelectedFoldersChanged: console.log("PM sel folders:", structurePM.selectedFolders) onDataChanged: { console.log("PM data changed") } } */ Timer { id: checkVersionTimer repeat : true triggeredOnStart: false interval : Style.main.verCheckRepeatTime onTriggered : go.runCheckVersion(false) } property string areYouSureYouWantToQuit : qsTr("There are incomplete processes - some items are not yet transferred. Do you really want to stop and quit?") // On start Component.onCompleted : { // set spell messages go.wrongCredentials = qsTr("Incorrect username or password." , "notification", -1) go.wrongMailboxPassword = qsTr("Incorrect mailbox password." , "notification", -1) go.canNotReachAPI = qsTr("Cannot contact server, please check your internet connection." , "notification", -1) go.versionCheckFailed = qsTr("Version check was unsuccessful. Please try again later." , "notification", -1) go.credentialsNotRemoved = qsTr("Credentials could not be removed." , "notification", -1) go.bugNotSent = qsTr("Unable to submit bug report." , "notification", -1) go.bugReportSent = qsTr("Bug report successfully sent." , "notification", -1) go.runCheckVersion(false) checkVersionTimer.start() gui.allMonths = getMonthList(1,12) gui.allMonthsChanged() } }