├── version.txt ├── bitmaps ├── hide.png ├── quit.png ├── ico_ok.ico ├── ico_ok.png ├── refresh.png ├── editfolder.png ├── encfsgui.icns ├── encfsgui.ico ├── encfsgui.png ├── folderinfo.png ├── ico_notok.ico ├── ico_notok.png ├── openfolder.png ├── settings.png ├── browsefolder.png ├── createfolder.png ├── mountfolder.png ├── removefolder.png ├── existingfolder.png ├── icons8-lock-24.png ├── icons8-lock-32.png ├── unmountfolder.png ├── icons8-protect-24.png ├── icons8-protect-32.png ├── icons8-unlock-24.png ├── icons8-unlock-32.png ├── unmountfolder_all.png ├── icons8-warning-shield-24.png ├── icons8-warning-shield-32.png ├── ico_notok.xpm ├── ico_ok.xpm └── mountfolder.xpm ├── pyencfsgui.sh ├── .gitignore ├── encfsgui_globals.py ├── cgetmasterkey.py ├── cmountpassword.py ├── cnewmasterkey.py ├── encfsgui_newmasterkey.ui ├── encfsgui_masterkey.ui ├── encfsgui_password.ui ├── README.md ├── encfsgui_settings.ui ├── csettings.py ├── cconfig.py ├── encfsgui_volume.ui ├── encfsgui_main.ui ├── encfsgui_helper.py └── cvolume.py /version.txt: -------------------------------------------------------------------------------- 1 | 3.0.4 -------------------------------------------------------------------------------- /bitmaps/hide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/hide.png -------------------------------------------------------------------------------- /bitmaps/quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/quit.png -------------------------------------------------------------------------------- /pyencfsgui.sh: -------------------------------------------------------------------------------- 1 | cd /Users/corelanc0d3r/pyencfsgui 2 | python3 encfsgui.py & 3 | exit 4 | -------------------------------------------------------------------------------- /bitmaps/ico_ok.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/ico_ok.ico -------------------------------------------------------------------------------- /bitmaps/ico_ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/ico_ok.png -------------------------------------------------------------------------------- /bitmaps/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/refresh.png -------------------------------------------------------------------------------- /bitmaps/editfolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/editfolder.png -------------------------------------------------------------------------------- /bitmaps/encfsgui.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/encfsgui.icns -------------------------------------------------------------------------------- /bitmaps/encfsgui.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/encfsgui.ico -------------------------------------------------------------------------------- /bitmaps/encfsgui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/encfsgui.png -------------------------------------------------------------------------------- /bitmaps/folderinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/folderinfo.png -------------------------------------------------------------------------------- /bitmaps/ico_notok.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/ico_notok.ico -------------------------------------------------------------------------------- /bitmaps/ico_notok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/ico_notok.png -------------------------------------------------------------------------------- /bitmaps/openfolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/openfolder.png -------------------------------------------------------------------------------- /bitmaps/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/settings.png -------------------------------------------------------------------------------- /bitmaps/browsefolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/browsefolder.png -------------------------------------------------------------------------------- /bitmaps/createfolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/createfolder.png -------------------------------------------------------------------------------- /bitmaps/mountfolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/mountfolder.png -------------------------------------------------------------------------------- /bitmaps/removefolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/removefolder.png -------------------------------------------------------------------------------- /bitmaps/existingfolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/existingfolder.png -------------------------------------------------------------------------------- /bitmaps/icons8-lock-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/icons8-lock-24.png -------------------------------------------------------------------------------- /bitmaps/icons8-lock-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/icons8-lock-32.png -------------------------------------------------------------------------------- /bitmaps/unmountfolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/unmountfolder.png -------------------------------------------------------------------------------- /bitmaps/icons8-protect-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/icons8-protect-24.png -------------------------------------------------------------------------------- /bitmaps/icons8-protect-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/icons8-protect-32.png -------------------------------------------------------------------------------- /bitmaps/icons8-unlock-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/icons8-unlock-24.png -------------------------------------------------------------------------------- /bitmaps/icons8-unlock-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/icons8-unlock-32.png -------------------------------------------------------------------------------- /bitmaps/unmountfolder_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/unmountfolder_all.png -------------------------------------------------------------------------------- /bitmaps/icons8-warning-shield-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/icons8-warning-shield-24.png -------------------------------------------------------------------------------- /bitmaps/icons8-warning-shield-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corelan/pyencfsgui/HEAD/bitmaps/icons8-warning-shield-32.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | encfsgui.settings 2 | encfsgui.volumes 3 | encfsgui.log 4 | runpyencfsgui.sh 5 | *_mnt 6 | *_enc 7 | old/ 8 | .vscode/ 9 | __pycache__/ 10 | pyencfsgui.code-workspace 11 | .DS_Store -------------------------------------------------------------------------------- /encfsgui_globals.py: -------------------------------------------------------------------------------- 1 | # globals and global classes 2 | global g_Volumes 3 | global g_Settings 4 | global g_Encodings 5 | global g_CurrentlySelected 6 | global appconfig 7 | global debugmode 8 | global settingsfile 9 | global volumesfile 10 | global logfile 11 | global app 12 | global masterkey 13 | global ishidden 14 | global timeswrong 15 | 16 | volumesfile = "encfsgui.volumes" 17 | logfile = "encfsgui.log" 18 | debugmode = False 19 | g_Encodings = [] 20 | ishidden = False 21 | timeswrong = 0 22 | 23 | class CEncryptedVolume: 24 | def __init__(self): 25 | #self.volumename = "" 26 | self.enc_path = "" 27 | self.mount_path = "" 28 | self.passwordsaved = 0 29 | self.encfsmountoptions = "" 30 | self.automount = 0 31 | self.preventautounmount = 0 32 | self.allowother = 0 33 | self.mountaslocal = 0 34 | self.ismounted = False 35 | self.enc_path_exists = True 36 | self.enctype = "encfs" 37 | self.sudo = 0 38 | self.errormessage = "" 39 | -------------------------------------------------------------------------------- /cgetmasterkey.py: -------------------------------------------------------------------------------- 1 | # encfsgui Ask for master key 2 | # 3 | 4 | import os 5 | import sys 6 | import time 7 | import datetime 8 | import string 9 | 10 | from PyQt5 import uic 11 | from PyQt5 import QtWidgets 12 | from PyQt5.QtWidgets import * 13 | from PyQt5 import QtCore 14 | from PyQt5.QtWidgets import QMessageBox, QSystemTrayIcon, QMenu, QAction, QStyle 15 | 16 | import encfsgui_globals 17 | from encfsgui_globals import * 18 | 19 | import encfsgui_helper 20 | from encfsgui_helper import * 21 | 22 | class CMasterKeyWindow(QtWidgets.QDialog): 23 | def __init__(self): 24 | encfsgui_helper.print_debug("Start CMasterKeyWindow %s" % inspect.stack()[0][3]) 25 | super(CMasterKeyWindow, self).__init__() 26 | uic.loadUi('encfsgui_masterkey.ui', self) 27 | # disable/remove buttons 28 | self.setWindowFlags(self.windowFlags() | QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowStaysOnTopHint) 29 | self.setWindowFlag(QtCore.Qt.WindowMaximizeButtonHint, False) 30 | self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) 31 | 32 | self.okbutton = self.findChild(QtWidgets.QPushButton, 'btn_OK') 33 | self.okbutton.clicked.connect(self.OKButtonClicked) 34 | 35 | self.cancelbutton = self.findChild(QtWidgets.QPushButton, 'btn_cancel') 36 | self.cancelbutton.clicked.connect(self.CancelButtonClicked) 37 | 38 | self.txt_password = self.findChild(QtWidgets.QLineEdit, 'txt_password') 39 | 40 | 41 | def getPassword(self): 42 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 43 | pw = encfsgui_helper.makePW32(self.txt_password.text()[0:31]) 44 | return pw 45 | 46 | def OKButtonClicked(self): 47 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 48 | self.close() 49 | return 50 | 51 | def CancelButtonClicked(self): 52 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 53 | self.txt_password.setText("") 54 | self.close() 55 | return -------------------------------------------------------------------------------- /cmountpassword.py: -------------------------------------------------------------------------------- 1 | # encfsgui Get mount password 2 | # 3 | # 4 | 5 | import os 6 | import sys 7 | import time 8 | import datetime 9 | import string 10 | 11 | from PyQt5 import uic 12 | from PyQt5 import QtWidgets 13 | from PyQt5.QtWidgets import * 14 | from PyQt5 import QtCore 15 | 16 | import encfsgui_globals 17 | from encfsgui_globals import * 18 | 19 | import encfsgui_helper 20 | from encfsgui_helper import * 21 | 22 | class CMountPasswordWindow(QtWidgets.QDialog): 23 | def __init__(self): 24 | encfsgui_helper.print_debug("Start CMountPasswordWindow %s" % inspect.stack()[0][3]) 25 | super(CMountPasswordWindow, self).__init__() 26 | uic.loadUi('encfsgui_password.ui', self) 27 | 28 | # disable/remove buttons 29 | self.setWindowFlags(self.windowFlags() | QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowStaysOnTopHint) 30 | self.setWindowFlag(QtCore.Qt.WindowMaximizeButtonHint, False) 31 | self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) 32 | 33 | self.lbl_enc_path = self.findChild(QtWidgets.QLabel, 'lbl_enc_path') 34 | self.lbl_mount_path = self.findChild(QtWidgets.QLabel, 'lbl_mount_path') 35 | self.txt_password = self.findChild(QtWidgets.QLineEdit, 'txt_password') 36 | 37 | self.okbutton = self.findChild(QtWidgets.QPushButton, 'btn_OK') 38 | self.okbutton.clicked.connect(self.OKButtonClicked) 39 | 40 | self.cancelbutton = self.findChild(QtWidgets.QPushButton, 'btn_cancel') 41 | self.cancelbutton.clicked.connect(self.CancelButtonClicked) 42 | 43 | def setEncPath(self, path): 44 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 45 | self.lbl_enc_path.setText(path) 46 | return 47 | 48 | def setMountPath(self, path): 49 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 50 | self.lbl_mount_path.setText(path) 51 | return 52 | 53 | def getPassword(self): 54 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 55 | return self.txt_password.text() 56 | 57 | def OKButtonClicked(self): 58 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 59 | self.close() 60 | return 61 | 62 | def CancelButtonClicked(self): 63 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 64 | self.txt_password.setText("") 65 | self.close() 66 | return -------------------------------------------------------------------------------- /bitmaps/ico_notok.xpm: -------------------------------------------------------------------------------- 1 | /* XPM */ 2 | static const char *ico_notok[] = { 3 | /* columns rows colors chars-per-pixel */ 4 | "32 32 54 1 ", 5 | " c #C6362A", 6 | ". c #C8372D", 7 | "X c #C7392E", 8 | "o c #CA3A2E", 9 | "O c #C93F33", 10 | "+ c #C94136", 11 | "@ c #CA443A", 12 | "# c #CB4A3F", 13 | "$ c #CD4E43", 14 | "% c #CD5146", 15 | "& c #CE544A", 16 | "* c #CF594E", 17 | "= c #D0574E", 18 | "- c #D0594F", 19 | "; c #D15D54", 20 | ": c #D3665D", 21 | "> c #D66E65", 22 | ", c #D57167", 23 | "< c #D6746C", 24 | "1 c #D9746B", 25 | "2 c #D9796F", 26 | "3 c #D77870", 27 | "4 c #DA7B74", 28 | "5 c #DC837D", 29 | "6 c #DC8780", 30 | "7 c #DE8C86", 31 | "8 c #DF8E88", 32 | "9 c #DF9089", 33 | "0 c #E0938D", 34 | "q c #E19790", 35 | "w c #E29B94", 36 | "e c #E49E98", 37 | "r c #E4A29C", 38 | "t c #E6A7A1", 39 | "y c #E7ACA7", 40 | "u c #E7ADA8", 41 | "i c #E8AEA9", 42 | "p c #E9B2AD", 43 | "a c #EAB6B1", 44 | "s c #EBB9B5", 45 | "d c #ECBDB9", 46 | "f c #EEC1BD", 47 | "g c #EFC6C2", 48 | "h c #F2D0CD", 49 | "j c #F3D3D0", 50 | "k c #F5DEDC", 51 | "l c #F7E3E1", 52 | "z c #F8E7E7", 53 | "x c #F9EAE9", 54 | "c c #FBF0EF", 55 | "v c #FCF2F1", 56 | "b c #F5FEFF", 57 | "n c #FEFDFD", 58 | "m c None", 59 | /* pixels */ 60 | "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm", 61 | "mmmmmmmmmmmmmmmO.mmmmmmmmmmmmmmm", 62 | "mmmmmmmmmmmmX-235>-Ommmmmmmmmmmm", 63 | "mmmmmmmmmmX>fnbbbbng:Xmmmmmmmmmm", 64 | "mmmmmmmmmX8nnkq50ilnn6Ommmmmmmmm", 65 | "mmmmmmmmX3nn8@XOXX@5nb3 mmmmmmmm", 66 | "mmmmmmmm@zn2X++mm++X,nv&mmmmmmmm", 67 | "mmmmmmmX8bpX+mmmmmm+Otn0 mmmmmmm", 68 | "mmmmmmmOhn:XmmmmmmmmO=nh+mmmmmmm", 69 | "mmmmmmm&nk++mmmmmmmmOOhn&mmmmmmm", 70 | "mmmmmmm>nfXmmmmmmmmmmXan;mmmmmmm", 71 | "mmmmmmm2brXmmmmmmmmmmo0x;mmmmmmm", 72 | "mmmmmmm,bwXmmmmmmmmmmmmmmmmmmmmm", 73 | "mmmmmmm:nw mmmmmmmmmmmmmmmmmmmmm", 74 | "mmmm;m>,u8:>,2233>2mm422253:mmmm", 75 | "mmm0cnnnnnnnnnnnnnnnnnnnnnnv5mmm", 76 | "mmmpnw6wwww088990wqw080qwwenpmmm", 77 | "mmmdn#XXXXXXXXXXXXXXXXXX &npmmm", 78 | "mmmfn;O+@+++++++++++@+++++*nymmm", 79 | "mmmfn-+++++++++++++++++++X*nummm", 80 | "mmmsn-O+++++++++++@++++++O&nummm", 81 | "mmmpn&O++++++++++++++++++O%nimmm", 82 | "mmmin&+++++++++++++++++++O&npmmm", 83 | "mmmin*X++++++++++++++++++O&npmmm", 84 | "mmmyn&O+@O+++++++++@OO@O+O&nammm", 85 | "mmmpn$ XX X. XXX X..O.OOXX%nymmm", 86 | "mmmanspaspadaaaadspatiiisppnrmmm", 87 | "mmm6lnnnnnnnnnnnnnnnnnvnnnnx5mmm", 88 | "mmmO@$&&&$$$$%=%$$$%$%%%$=&#+mmm", 89 | "mm++++OOOO+O+OOOOO+OOOOOOO++++mm", 90 | "mmmmmmmmmmmm+O@+O+++@@O+mmmmmmmm", 91 | "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" 92 | }; 93 | -------------------------------------------------------------------------------- /cnewmasterkey.py: -------------------------------------------------------------------------------- 1 | # encfsgui Create a new master key 2 | # 3 | 4 | import os 5 | import sys 6 | import time 7 | import datetime 8 | import string 9 | 10 | from PyQt5 import uic 11 | from PyQt5 import QtWidgets 12 | from PyQt5.QtWidgets import * 13 | from PyQt5 import QtCore 14 | from PyQt5.QtWidgets import QMessageBox, QSystemTrayIcon, QMenu, QAction, QStyle 15 | 16 | import encfsgui_globals 17 | from encfsgui_globals import * 18 | 19 | import encfsgui_helper 20 | from encfsgui_helper import * 21 | 22 | class CNewMasterKeyWindow(QtWidgets.QDialog): 23 | def __init__(self): 24 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 25 | super(CNewMasterKeyWindow, self).__init__() 26 | uic.loadUi('encfsgui_newmasterkey.ui', self) 27 | # disable/remove buttons 28 | self.setWindowFlags(self.windowFlags() | QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowStaysOnTopHint) 29 | self.setWindowFlag(QtCore.Qt.WindowMaximizeButtonHint, False) 30 | self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) 31 | 32 | self.okbutton = self.findChild(QtWidgets.QPushButton, 'btn_OK') 33 | self.okbutton.clicked.connect(self.OKButtonClicked) 34 | 35 | self.cancelbutton = self.findChild(QtWidgets.QPushButton, 'btn_cancel') 36 | self.cancelbutton.clicked.connect(self.CancelButtonClicked) 37 | 38 | self.txt_password1 = self.findChild(QtWidgets.QLineEdit, 'txt_password1') 39 | self.txt_password2 = self.findChild(QtWidgets.QLineEdit, 'txt_password2') 40 | 41 | 42 | def getPassword(self): 43 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 44 | # make sure password is exactly 32 bytes 45 | pw = encfsgui_helper.makePW32(self.txt_password1.text()[0:31]) 46 | return pw 47 | 48 | def OKButtonClicked(self): 49 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 50 | # check if both passwords are the same 51 | 52 | errorsfound = False 53 | errortext = "" 54 | if (self.txt_password1.text() != self.txt_password2.text()): 55 | errorsfound = True 56 | errortext = "- Both passwords are not the same\n" 57 | if (self.txt_password1.text() == "" or self.txt_password2.text() == ""): 58 | errorsfound = True 59 | errortext = "- Password cannot be empty\n" 60 | if (len(self.txt_password1.text()) < 8): 61 | errorsfound = True 62 | errortext = "- Password must be 8 characters or longer\n" 63 | if not errorsfound: 64 | self.close() 65 | else: 66 | errorMsgBox = QtWidgets.QMessageBox() 67 | errorMsgBox.setWindowTitle("Errors found") 68 | errorMsgBox.setText("Unable to save password:\n\n" + errortext) 69 | errorMsgBox.setIcon(QMessageBox.Critical) 70 | errorMsgBox.show() 71 | errorMsgBox.exec_() 72 | return 73 | 74 | def CancelButtonClicked(self): 75 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 76 | self.txt_password1.setText("") 77 | self.txt_password2.setText("") 78 | self.close() 79 | return -------------------------------------------------------------------------------- /encfsgui_newmasterkey.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 358 13 | 160 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 358 25 | 160 26 | 27 | 28 | 29 | 30 | 358 31 | 160 32 | 33 | 34 | 35 | Dialog 36 | 37 | 38 | true 39 | 40 | 41 | 42 | 43 | 20 44 | 10 45 | 311 46 | 16 47 | 48 | 49 | 50 | Please enter the master password: 51 | 52 | 53 | 54 | 55 | 56 | 20 57 | 60 58 | 311 59 | 16 60 | 61 | 62 | 63 | Verify master password: 64 | 65 | 66 | 67 | 68 | 69 | 20 70 | 30 71 | 311 72 | 21 73 | 74 | 75 | 76 | QLineEdit::Password 77 | 78 | 79 | 80 | 81 | 82 | 20 83 | 80 84 | 311 85 | 21 86 | 87 | 88 | 89 | QLineEdit::Password 90 | 91 | 92 | 93 | 94 | 95 | 20 96 | 120 97 | 113 98 | 32 99 | 100 | 101 | 102 | OK 103 | 104 | 105 | 106 | 107 | 108 | 220 109 | 120 110 | 113 111 | 32 112 | 113 | 114 | 115 | Cancel 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /encfsgui_masterkey.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 358 13 | 100 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 358 25 | 100 26 | 27 | 28 | 29 | 30 | 358 31 | 100 32 | 33 | 34 | 35 | Dialog 36 | 37 | 38 | true 39 | 40 | 41 | 42 | 43 | 70 44 | 10 45 | 241 46 | 20 47 | 48 | 49 | 50 | Please enter the master password: 51 | 52 | 53 | 54 | 55 | 56 | 70 57 | 30 58 | 271 59 | 21 60 | 61 | 62 | 63 | QLineEdit::Password 64 | 65 | 66 | 67 | 68 | 69 | 20 70 | 60 71 | 113 72 | 32 73 | 74 | 75 | 76 | OK 77 | 78 | 79 | 80 | 81 | 82 | 220 83 | 60 84 | 113 85 | 32 86 | 87 | 88 | 89 | Cancel 90 | 91 | 92 | 93 | 94 | 95 | 10 96 | 10 97 | 51 98 | 51 99 | 100 | 101 | 102 | 103 | 0 104 | 0 105 | 106 | 107 | 108 | 109 | 32 110 | 32 111 | 112 | 113 | 114 | 115 | 32 116 | 32 117 | 118 | 119 | 120 | 121 | 122 | 123 | bitmaps/encfsgui.ico 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /encfsgui_password.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | frmEnterPassword 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 470 13 | 160 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 470 25 | 160 26 | 27 | 28 | 29 | 30 | 470 31 | 160 32 | 33 | 34 | 35 | Qt::StrongFocus 36 | 37 | 38 | Please enter password 39 | 40 | 41 | true 42 | 43 | 44 | 45 | 46 | 50 47 | 10 48 | 221 49 | 16 50 | 51 | 52 | 53 | Please enter password to mount 54 | 55 | 56 | 57 | 58 | 59 | 50 60 | 30 61 | 411 62 | 16 63 | 64 | 65 | 66 | 67 | 12 68 | 75 69 | true 70 | 71 | 72 | 73 | lbl_enc_path 74 | 75 | 76 | 77 | 78 | 79 | 50 80 | 50 81 | 221 82 | 16 83 | 84 | 85 | 86 | as 87 | 88 | 89 | 90 | 91 | 92 | 50 93 | 70 94 | 411 95 | 16 96 | 97 | 98 | 99 | 100 | 12 101 | 75 102 | true 103 | 104 | 105 | 106 | lbl_mount_path 107 | 108 | 109 | 110 | 111 | 112 | 60 113 | 100 114 | 331 115 | 21 116 | 117 | 118 | 119 | QLineEdit::Password 120 | 121 | 122 | 123 | 124 | 125 | 10 126 | 130 127 | 113 128 | 32 129 | 130 | 131 | 132 | OK 133 | 134 | 135 | 136 | 137 | 138 | 340 139 | 130 140 | 113 141 | 32 142 | 143 | 144 | 145 | Cancel 146 | 147 | 148 | 149 | 150 | 151 | 10 152 | 10 153 | 32 154 | 32 155 | 156 | 157 | 158 | 159 | 160 | 161 | bitmaps/icons8-lock-32.png 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /bitmaps/ico_ok.xpm: -------------------------------------------------------------------------------- 1 | /* XPM */ 2 | static const char *ico_ok[] = { 3 | /* columns rows colors chars-per-pixel */ 4 | "32 32 200 2 ", 5 | " c #40831B", 6 | ". c #458620", 7 | "X c #478824", 8 | "o c #488825", 9 | "O c #498927", 10 | "+ c #498A26", 11 | "@ c #4A8A27", 12 | "# c #4A8928", 13 | "$ c #4A8929", 14 | "% c #4A8A28", 15 | "& c #4B8A28", 16 | "* c #4B8A29", 17 | "= c #4B8B2A", 18 | "- c #4C8A29", 19 | "; c #4C8B2A", 20 | ": c #4D8A2B", 21 | "> c #4D8B2B", 22 | ", c #4C8C2B", 23 | "< c #4D8C2B", 24 | "1 c #4D8B2C", 25 | "2 c #4E8B2C", 26 | "3 c #4D8C2C", 27 | "4 c #4E8C2C", 28 | "5 c #4F8C2D", 29 | "6 c #4F8D2D", 30 | "7 c #4F8D2E", 31 | "8 c #4F8D2F", 32 | "9 c #4F8E2E", 33 | "0 c #508D2E", 34 | "q c #518D2E", 35 | "w c #508D2F", 36 | "e c #518D2F", 37 | "r c #508E2F", 38 | "t c #518E2F", 39 | "y c #528E2F", 40 | "u c #508D30", 41 | "i c #518D30", 42 | "p c #518D31", 43 | "a c #508E30", 44 | "s c #518E30", 45 | "d c #518F30", 46 | "f c #508E31", 47 | "g c #518E31", 48 | "h c #528E30", 49 | "j c #528F30", 50 | "k c #528E31", 51 | "l c #528F31", 52 | "z c #528F32", 53 | "x c #529030", 54 | "c c #569136", 55 | "v c #569336", 56 | "b c #579238", 57 | "n c #589339", 58 | "m c #5A943A", 59 | "M c #59923C", 60 | "N c #5E9640", 61 | "B c #609742", 62 | "V c #619742", 63 | "C c #619943", 64 | "Z c #639A45", 65 | "A c #649A46", 66 | "S c #649B47", 67 | "D c #679B48", 68 | "F c #669B49", 69 | "G c #679C4A", 70 | "H c #679D4B", 71 | "J c #699E4D", 72 | "K c #6A9E4D", 73 | "L c #6B9F4E", 74 | "P c #6C9F51", 75 | "I c #6CA051", 76 | "U c #6DA052", 77 | "Y c #6EA153", 78 | "T c #6FA254", 79 | "R c #70A255", 80 | "E c #71A356", 81 | "W c #73A457", 82 | "Q c #74A65B", 83 | "! c #76A65D", 84 | "~ c #78A75F", 85 | "^ c #7CA962", 86 | "/ c #7CA963", 87 | "( c #7EAC67", 88 | ") c #81AC68", 89 | "_ c #80AD68", 90 | "` c #82AE6B", 91 | "' c #84AE6E", 92 | "] c #85AF6E", 93 | "[ c #84B16E", 94 | "{ c #87B171", 95 | "} c #89B272", 96 | "| c #89B372", 97 | " . c #8AB374", 98 | ".. c #8BB374", 99 | "X. c #8BB576", 100 | "o. c #8CB575", 101 | "O. c #8DB578", 102 | "+. c #90B77B", 103 | "@. c #91B77B", 104 | "#. c #93B97E", 105 | "$. c #92B980", 106 | "%. c #94BA81", 107 | "&. c #98BB83", 108 | "*. c #99BD86", 109 | "=. c #9CBF88", 110 | "-. c #9CBE89", 111 | ";. c #9CBF89", 112 | ":. c #9CBF8A", 113 | ">. c #9DBF8A", 114 | ",. c #9EBF8B", 115 | "<. c #9DC08A", 116 | "1. c #A0C18E", 117 | "2. c #A2C390", 118 | "3. c #A2C291", 119 | "4. c #A4C493", 120 | "5. c #A7C697", 121 | "6. c #A9C698", 122 | "7. c #A9C89A", 123 | "8. c #ACC99C", 124 | "9. c #ADCA9E", 125 | "0. c #AFCB9E", 126 | "q. c #AFCB9F", 127 | "w. c #AFCBA0", 128 | "e. c #B6CFA8", 129 | "r. c #B7D1AA", 130 | "t. c #BAD3AC", 131 | "y. c #BAD2AD", 132 | "u. c #BCD2AF", 133 | "i. c #BCD3AF", 134 | "p. c #BDD4B1", 135 | "a. c #BED5B1", 136 | "s. c #BDD5B2", 137 | "d. c #BED5B3", 138 | "f. c #BFD5B3", 139 | "g. c #BED6B2", 140 | "h. c #BFD6B3", 141 | "j. c #C0D7B4", 142 | "k. c #C1D7B5", 143 | "l. c #C5DABB", 144 | "z. c #C9DCBF", 145 | "x. c #CBDDC1", 146 | "c. c #D0E1C6", 147 | "v. c #D0E0C7", 148 | "b. c #D1E0C7", 149 | "n. c #D1E3C8", 150 | "m. c #D2E2C9", 151 | "M. c #D3E3CB", 152 | "N. c #D8E6D1", 153 | "B. c #DBE7D3", 154 | "V. c #DBE8D4", 155 | "C. c #DDE8D7", 156 | "Z. c #DDE9D7", 157 | "A. c #DFEAD9", 158 | "S. c #DFEBD9", 159 | "D. c #E0EBDA", 160 | "F. c #E1EBDB", 161 | "G. c #E1ECDB", 162 | "H. c #E4EEDE", 163 | "J. c #E4EEDF", 164 | "K. c #E6EFE0", 165 | "L. c #E6EFE2", 166 | "P. c #E9F3E3", 167 | "I. c #E9F1E4", 168 | "U. c #E9F1E5", 169 | "Y. c #EAF1E5", 170 | "T. c #E9F1E6", 171 | "R. c #EAF2E7", 172 | "E. c #EBF3E7", 173 | "W. c #EDF4EA", 174 | "Q. c #EEF4EC", 175 | "!. c #EEF5EC", 176 | "~. c #EFF5ED", 177 | "^. c #F1F7EF", 178 | "/. c #F2F6EE", 179 | "(. c #F2F6F0", 180 | "). c #F2F7F1", 181 | "_. c #F3F7F1", 182 | "`. c #F4F8F2", 183 | "'. c #F4F9F2", 184 | "]. c #F6F9F3", 185 | "[. c #F5F8F4", 186 | "{. c #F6FAF4", 187 | "}. c #F7FAF6", 188 | "|. c #F9FBF7", 189 | " X c #FCF7FF", 190 | ".X c #FAFBF9", 191 | "XX c #FAFCF9", 192 | "oX c #FCFEFB", 193 | "OX c #FDF9FF", 194 | "+X c #FDFBFF", 195 | "@X c #FEFBFF", 196 | "#X c #FDFEFD", 197 | "$X c #FDFCFF", 198 | "%X c #FEFCFF", 199 | "&X c #FEFDFF", 200 | "*X c #FFFDFF", 201 | "=X c #FEFEFE", 202 | "-X c #FFFEFF", 203 | ";X c white", 204 | ":X c None", 205 | /* pixels */ 206 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X", 207 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X", 208 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X", 209 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X", 210 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X", 211 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:XM :X:X:X:X:X:X:X:X:X:X", 212 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X` A.M.o.:X:X:X:X:X:X:X:X", 213 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:XU P.;X;X}.2.:X:X:X:X:X:X:X", 214 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:Xi.-X@.E Z.-Xj.K :X:X:X:X:X", 215 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:XX.-Xd.> * z 0.&XR.&.:X:X:X:X", 216 | ":X:X:X:X:X:X:X:XR :X:X:X:X:X:X:X:XJ E.K.b 9 s 8 o _ Q.#X7.:X:X:X", 217 | ":X:X:X:X:X:X:X<.^.a.:X:X:X:X:X:X:Xh.;XT , d s d h 6.;Xs.:X:X:X", 218 | ":X:X:X:X:X:X! _.|.%XU.$.:X:X:X:X%.%X>.O d d d g < H '.Y.W :X:X:X", 219 | ":X:X:X:X:X:XN.{.Y } /.=Xw.:X:X] {.m.5 a d s i r 6 n.=X#.:X:X:X:X", 220 | ":X:X:X:X:Xe.%X| # # V z.&XV.t.~.`.Z 2 h t t j @ *.%Xr.:X:X:X:X:X", 221 | ":X:X:X:X..-Xu.$ d i 1 = 1..XOX;X .+ g k s s < I XXB.:X:X:X:X:X:X", 222 | ":X:X:XA L.E.m 7 d d s w O P G.g.% h j s s 0 r H.]./ :X:X:X:X:X:X", 223 | ":X:X:X8.@X:.. s d s s s d < b c q d s s i % w.$X=.:X:X:X:X:X:X:X", 224 | ":X:X:XO..X`.+.& 7 j s j s f 7 0 i s s t % ( %Xp.:X:X:X:X:X:X:X:X", 225 | ":X:X:X:X) D.+Xi.v & s s d t s t s s j 5 B ).F.C :X:X:X:X:X:X:X:X", 226 | ":X:X:X:X:XN q.-XJ.^ X w s t s s s s a - b.oX' :X:X:X:X:X:X:X:X:X", 227 | ":X:X:X:X:X:X:X} W.-X5.6 3 g h t s d O 4.@X4.:X:X:X:X:X:X:X:X:X:X", 228 | ":X:X:X:X:X:X:X:XL x.%Xv.G O s s s ; Q ;Xl.:X:X:X:X:X:X:X:X:X:X:X", 229 | ":X:X:X:X:X:X:X:X:X:X3.XX[.;.: 1 7 n T.I.D :X:X:X:X:X:X:X:X:X:X:X", 230 | ":X:X:X:X:X:X:X:X:X:X:X[ S.+Xc.F % f.&X{ :X:X:X:X:X:X:X:X:X:X:X:X", 231 | ":X:X:X:X:X:X:X:X:X:X:X:X:Xy.;X(.k.*X9.:X:X:X:X:X:X:X:X:X:X:X:X:X", 232 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X,.!. XC.:X:X:X:X:X:X:X:X:X:X:X:X:X:X", 233 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:XS -.~ :X:X:X:X:X:X:X:X:X:X:X:X:X:X", 234 | ":X:Xx u t t p s i r s t j s s 7 * 4 h h s d s h s a y t d p :X:X", 235 | ":X:Xl a s s a s d h s s s s i s s e s g s h s t i s s i s x :X:X", 236 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X", 237 | ":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X" 238 | }; 239 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyencfsgui 2 | 3 | ## What is pyencfsgui? 4 | 5 | pyencfsgui is a Qt based GUI/wrapper around EncFS (`encfs`, `encfsctl`) and GoCryptFS (`gocryptfs`), `mount` and `umount`. 6 | It is written in python3, and relies on `encfs`/`gocryptfs` to be able to mount an enrypted folder. (These utilities typically rely on OSXFuse/MacFuse/...Fuse to provide a filesystem.)
7 | In other words, pyencfsgui simply provides a GUI that uses the aformentioned utilities in the backend, and relies on the ability to launch these binaries, to interact with them and to capture & parse the output from those tools.
8 | As a result, the EncFSGui source code is pretty easy to understand, as it does not contain any crypto or other black magic to do its job. The downside is that it is a wrapper and may break if tools start behaving in a different way.
9 | 10 | pyencfsgui was developed on macOS using encfs versions 1.8.x and 1.9.x., and gocryptfs version 1.8.x
11 | 12 | pyencfsgui was tested on recent version of macOS and Kali Linux
13 | 14 | ## Dependencies 15 | 16 | In order for pyencfsgui to be able to function, you need to install the following dependencies: 17 | 18 | - python3 (3.9.x or higher) 19 | - python3 libraries: PyQT5, pycrypto 20 | 21 | Of course, as pyencfsgui relies on encfs and/or gocryptfs, those utilities need to be installed as well. 22 | - encfs (1.9.x) and/or gocryptfs (1.8.x) 23 | 24 | On macOS, you'll also need to install: 25 | - Developer Command Line Tools 26 | - OSXFuse/macFuse (depending on what encfs/gocryptfs needs) 27 | 28 | Kali Linux already has most dependencies installed. If you're using a different distro, you may have to install some utilities as needed. 29 | (If you're using xfce as Desktop Manager, you may want to install & run something like `plank` to see application icons at the bottom of your screen) 30 | 31 | ### Note: April 16 2021 - encfs/gocryptfs brew recipe issues 32 | 33 | It appears that the brew recipes for encfs and gocryptfs are currently disabled 34 | 35 | https://github.com/Homebrew/discussions/discussions/1250 36 | https://github.com/vgough/encfs/issues/629 37 | https://github.com/vgough/encfs/issues/630 38 | 39 | The homebrew-based procedure below used to work fine until a few days ago. If you prefer to use Homebrew, you'll have to wait until someone addresses the issues. 40 | If you already have encfs/gocryptfs installed and working, then you can skip those parts of the installation procedure below. 41 | 42 | Of course, you can also try to install encfs via MacPorts. You may have to compile gocryptfs yourself. 43 | 44 | 45 | ### Installing dependencies on macOS 46 | 47 | #### 1. Install Homebrew / MacPorts 48 | 49 | If you'd like to use Homebrew: 50 | 51 | ``` 52 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 53 | sudo chown -R $(whoami) /usr/local/lib/pkgconfig 54 | brew doctor 55 | ``` 56 | 57 | If you prefer to use MacPorts instead, check installation instructions at https://www.macports.org/install.php 58 | 59 | 60 | #### 2. Install OSXFuse/MacFuse 61 | 62 | Download the latest dmg installer image from https://osxfuse.github.io/ and run the installer. 63 | 64 | If you're running a macOS version that doesn't have a working OSXFuse installer (yet), you can also try to install osxfuse using brew: 65 | 66 | ``` 67 | brew tap homebrew/cask 68 | brew install --cask osxfuse 69 | ``` 70 | 71 | Next, 72 | 73 | - Make sure to enable the kernel extension in System Preferences → Security & Privacy → General if/as requested 74 | - Reboot for osxfuse to work correctly. 75 | 76 | Note: on recent Mac devices (with M1 processor) running Big Sur or later, you may have to allow Kernel Extensions using the Startup Security Utility: 77 | 78 | - Shutdown 79 | - Press & hold Touch ID & power button. Boot into recovery mode 80 | - Launch Startup Security Utility 81 | - Allow third party "kernel extensions" by changing the "Security Policy" to "Reduced Security" 82 | 83 | 84 | #### 3. Install encfs / gocryptfs 85 | 86 | ##### 3.1 Encfs 87 | 88 | If you're using Homebrew: 89 | ``` 90 | brew update 91 | ``` 92 | 93 | Check if everything is ok and install encfs 94 | 95 | ``` 96 | brew doctor 97 | brew install encfs 98 | ``` 99 | 100 | Check if encfs works: 101 | ``` 102 | encfs 103 | ``` 104 | 105 | If you're using MacPorts: 106 | 107 | ``` 108 | sudo port selfupdate 109 | sudo port install encfs 110 | ``` 111 | 112 | Check if encfs works: 113 | ``` 114 | encfs 115 | ``` 116 | 117 | ##### 3.2 GoCryptFS 118 | 119 | Homebrew: 120 | ``` 121 | brew update 122 | ``` 123 | 124 | Check if everything is ok and install encfs 125 | 126 | ``` 127 | brew doctor 128 | brew install gocryptfs 129 | ``` 130 | 131 | Check if encfs works: 132 | ``` 133 | gocryptfs 134 | ``` 135 | 136 | On a M1 Mac, check out this Github issue for instructions on how to compile gocryptfs: https://github.com/rfjakob/gocryptfs/issues/556#issuecomment-845810085 137 | 138 | 139 | #### 4. Install python3 140 | 141 | Homebrew: 142 | ``` 143 | brew install python3 144 | ``` 145 | (Make sure you're running a recent version of python3. Version 3.9.x or higher should work) 146 | 147 | 148 | #### 5. Install PyQt5 149 | ``` 150 | pip3 install --upgrade pip --user 151 | python3 -m pip install PyQt5 --user 152 | ``` 153 | 154 | Note: On my 2020 MacBook Air (M1 processor), I had to install PyQT5 using the following command instead: 155 | 156 | Homebrew: 157 | ``` 158 | brew install PyQt5 159 | ``` 160 | 161 | #### 6. Install macOS Developer Command Line Tools: 162 | 163 | ``` 164 | xcode-select --install 165 | ``` 166 | 167 | #### 7. Install pycrypto 168 | ``` 169 | python3 -m pip install pycrypto --user --break-system-packages 170 | ``` 171 | 172 | If `pycrypto` is installed, but you are still getting the error `*** Error importing Ramdom from Crypto ***` then try this: 173 | 174 | ``` 175 | python3 -m pip uninstall pycrypto 176 | python3 -m pip install pycryptodome --break-system-packages 177 | ``` 178 | 179 | #### 8. Jura font 180 | 181 | Download & install the Jura font from here: https://fontsov.com/font/juraregular.html 182 | 183 | 184 | 185 | 186 | ### Installing dependencies on Linux (tested on Kali) 187 | 188 | On Kali Linux, python3, PyQT5 and pycrypto should already be installed. 189 | 190 | #### 1. Jura font 191 | 192 | ``` 193 | sudo apt install fonts-jura 194 | ``` 195 | 196 | #### 2. Install encfs / gocryptfs 197 | 198 | ##### 2.1 Encfs 199 | 200 | ``` 201 | sudo apt install encfs 202 | ``` 203 | 204 | ##### 2.2 GoCryptFS 205 | 206 | ``` 207 | sudo apt install gocryptfs 208 | ``` 209 | 210 | 211 | 212 | 213 | ### Running pyencfsgui 214 | 215 | - Clone the git project onto your machine 216 | ``` 217 | git clone https://github.com/corelan/pyencfsgui.git 218 | ``` 219 | - Open a Terminal, go to the `pyencfsgui`folder 220 | - run `python3 encfsgui.py` 221 | - Check/edit the settings as needed. Make sure to verify the path of the various binaries (`encfs`, `gocryptfs`, `mount`) 222 | - Create a new volume (or add an existing one to the application) 223 | - Enjoy! 224 | 225 | 226 | ### Can I add a shortcut to the app in my Dock? 227 | 228 | Sure! Simply follow these steps: 229 | - Edit file pyencfsgui.sh and replace `path_to_pyencfsgui_here` (first line) with the full path to the folder where you have put the pyencfsgui repository 230 | - Open a Terminal and go to the folder than contains the pyencfsgui repository 231 | - Make the script executable: 232 | ``` 233 | chmod +x pyencfsgui.sh 234 | ```` 235 | - Rename the script to pyencfsgui.app 236 | ``` 237 | mv pyencfsgui.sh pyencfsgui.app 238 | ``` 239 | - Open Finder, go to the folder that contains the repository, and drag the .app file into your dock 240 | - In Terminal, rename the file back to .sh 241 | ``` 242 | mv pyencfsgui.app pyencfsgui.sh 243 | ``` 244 | - Open Finder, go to the folder that contains the repository, select the pyencfsgui.sh script, right-click and choose "Get Info" 245 | - Make sure the script will open with "Terminal" 246 | 247 | (it might be a good idea to also check/confirm that Terminal will close itself when the script exits) 248 | 249 | Bonus: if you would like to use the encfsgui icon for the shortcut in Dock, follow these steps: 250 | - Open "encfsgui.png" in the bitmaps folder 251 | - Use Cmd+A to select the image, and then Cmd+C to copy it to clipboard 252 | - Launch finder, open the folder that contains the pyencfsgui.sh script. Select the file, right-click and choose "Get Info" 253 | - Select the icon in the upper left corner of the Info window. Then press Cmd+V to paste the image. 254 | 255 | 256 | ## Known issues 257 | 258 | ### Character limitations for passwords 259 | 260 | You're not supposed to use a single-tick (') or exclamation mark (!) in the password for new volumes. It may cause the 'expect' script to fail, and/or might end up setting a different password on the volume. If you insist on using a single-tick or exclamation mark, simply create the volume with encfs yourself, and then add the volume to the app (as opposed to creating it in the app itself) 261 | 262 | 263 | ### GoCryptFS limitations 264 | 265 | #### Custom volumename for GoCryptFS folders 266 | 267 | GoCryptFS doesn't seem to support the ability to mount folders, specifying a custom volume name. As a result, the mounted volume will simply have the name of the folder it is mounted at. 268 | (Choose your folder names wisely!) 269 | 270 | ### Linux Desktop Manager limitations 271 | 272 | #### Hide to System Tray 273 | 274 | Some Linux Desktop Managers will not show the system tray icon when "hiding" pyencfsgui. This appears to be a PyQt5/Linux Desktop Manager compatibility issue. 275 | On Linux I decided to "minimize" the GUI rather than "hide" it, as you may not be able to "unhide" it (due to lack of a system tray icon you can interact with). 276 | -------------------------------------------------------------------------------- /encfsgui_settings.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | frmSettings 4 | 5 | 6 | Qt::WindowModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 650 13 | 670 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 650 25 | 670 26 | 27 | 28 | 29 | 30 | 650 31 | 670 32 | 33 | 34 | 35 | 36 | 650 37 | 670 38 | 39 | 40 | 41 | Qt::StrongFocus 42 | 43 | 44 | encfsgui Settings 45 | 46 | 47 | true 48 | 49 | 50 | 51 | 52 | 400 53 | 630 54 | 113 55 | 32 56 | 57 | 58 | 59 | &OK 60 | 61 | 62 | 63 | 64 | 65 | 520 66 | 630 67 | 113 68 | 32 69 | 70 | 71 | 72 | &Cancel 73 | 74 | 75 | 76 | 77 | 78 | 20 79 | 10 80 | 171 81 | 16 82 | 83 | 84 | 85 | Full path to 'encfs' binary: 86 | 87 | 88 | 89 | 90 | 91 | 20 92 | 110 93 | 381 94 | 16 95 | 96 | 97 | 98 | Full path to 'mount' binary: 99 | 100 | 101 | 102 | 103 | 104 | 20 105 | 160 106 | 181 107 | 16 108 | 109 | 110 | 111 | Full path to 'umount' binary: 112 | 113 | 114 | 115 | 116 | 117 | 20 118 | 30 119 | 481 120 | 21 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 20 134 | 130 135 | 481 136 | 21 137 | 138 | 139 | 140 | 141 | 142 | 143 | 20 144 | 180 145 | 481 146 | 21 147 | 148 | 149 | 150 | 151 | 152 | 153 | 520 154 | 20 155 | 113 156 | 40 157 | 158 | 159 | 160 | Select 161 | 162 | 163 | 164 | 165 | 166 | 520 167 | 120 168 | 113 169 | 40 170 | 171 | 172 | 173 | Select 174 | 175 | 176 | 177 | 178 | 179 | 520 180 | 170 181 | 113 182 | 40 183 | 184 | 185 | 186 | Select 187 | 188 | 189 | 190 | 191 | 192 | 20 193 | 210 194 | 501 195 | 16 196 | 197 | 198 | 199 | Working folder for 'volume' configuration file (empty or . = current folder) 200 | 201 | 202 | 203 | 204 | 205 | 20 206 | 230 207 | 481 208 | 21 209 | 210 | 211 | 212 | 213 | 214 | 215 | 520 216 | 220 217 | 113 218 | 40 219 | 220 | 221 | 222 | Select 223 | 224 | 225 | 226 | 227 | 228 | 20 229 | 630 230 | 291 231 | 20 232 | 233 | 234 | 235 | Debug mode (use only when requested) 236 | 237 | 238 | 239 | 240 | 241 | 20 242 | 260 243 | 611 244 | 121 245 | 246 | 247 | 248 | Startup && exit 249 | 250 | 251 | false 252 | 253 | 254 | 255 | 256 | 10 257 | 50 258 | 581 259 | 20 260 | 261 | 262 | 263 | Update at startup (git pull) 264 | 265 | 266 | 267 | 268 | 269 | 10 270 | 30 271 | 591 272 | 20 273 | 274 | 275 | 276 | Hide to system tray at startup 277 | 278 | 279 | 280 | 281 | 282 | 10 283 | 70 284 | 581 285 | 20 286 | 287 | 288 | 289 | Auto unmount volumes when closing app 290 | 291 | 292 | 293 | 294 | 295 | 10 296 | 90 297 | 591 298 | 20 299 | 300 | 301 | 302 | Do not prompt for confirmation on exit 303 | 304 | 305 | 306 | 307 | 308 | 309 | 20 310 | 380 311 | 611 312 | 101 313 | 314 | 315 | 316 | Mount && unmount volumes 317 | 318 | 319 | 320 | 321 | 10 322 | 30 323 | 581 324 | 20 325 | 326 | 327 | 328 | Do not prompt for confirmation when unmounting volume 329 | 330 | 331 | 332 | 333 | 334 | 10 335 | 50 336 | 591 337 | 20 338 | 339 | 340 | 341 | Prompt for confirmation when using 'force unmount all' 342 | 343 | 344 | 345 | 346 | 347 | 10 348 | 70 349 | 591 350 | 20 351 | 352 | 353 | 354 | Allow double-click in table to mount && unmount 355 | 356 | 357 | 358 | 359 | 360 | 361 | 20 362 | 480 363 | 611 364 | 131 365 | 366 | 367 | 368 | Security && Privacy 369 | 370 | 371 | 372 | 373 | 10 374 | 30 375 | 581 376 | 20 377 | 378 | 379 | 380 | Protect/encrypt 'volume' configuration file with a master password 381 | 382 | 383 | 384 | 385 | 386 | 10 387 | 50 388 | 591 389 | 31 390 | 391 | 392 | 393 | Clear master password when application is 'hidden' 394 | 395 | 396 | 397 | 398 | 399 | 30 400 | 80 401 | 571 402 | 16 403 | 404 | 405 | 406 | (Warning, this will also disable 'Volumes' menu when application is 'hidden') 407 | 408 | 409 | 410 | 411 | 412 | 10 413 | 100 414 | 591 415 | 20 416 | 417 | 418 | 419 | Hide volume if location of encrypted folder cannot be found 420 | 421 | 422 | 423 | 424 | 425 | 426 | 20 427 | 60 428 | 231 429 | 16 430 | 431 | 432 | 433 | Full path to 'gocryptfs' binary: 434 | 435 | 436 | 437 | 438 | 439 | 20 440 | 80 441 | 481 442 | 21 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 520 456 | 70 457 | 113 458 | 40 459 | 460 | 461 | 462 | Select 463 | 464 | 465 | 466 | 467 | 468 | 469 | -------------------------------------------------------------------------------- /csettings.py: -------------------------------------------------------------------------------- 1 | # encfsgui Settings 2 | # 3 | # 4 | 5 | import os 6 | import sys 7 | import time 8 | import datetime 9 | import string 10 | import configparser 11 | 12 | from PyQt5 import uic 13 | from PyQt5 import QtWidgets 14 | from PyQt5.QtWidgets import * 15 | from PyQt5 import QtCore 16 | 17 | import encfsgui_globals 18 | from encfsgui_globals import * 19 | 20 | import encfsgui_helper 21 | from encfsgui_helper import * 22 | 23 | import cconfig 24 | from cconfig import CConfig 25 | 26 | import cnewmasterkey 27 | from cnewmasterkey import CNewMasterKeyWindow 28 | 29 | 30 | class CSettingsWindow(QtWidgets.QDialog): 31 | def __init__(self): 32 | encfsgui_helper.print_debug("Start CSettingsWindow %s" % inspect.stack()[0][3]) 33 | super(CSettingsWindow, self).__init__() 34 | uic.loadUi('encfsgui_settings.ui', self) 35 | 36 | # disable/remove buttons 37 | self.setWindowFlags(self.windowFlags() | QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowStaysOnTopHint) 38 | self.setWindowFlag(QtCore.Qt.WindowMaximizeButtonHint, False) 39 | self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) 40 | 41 | # assign methods to buttons 42 | self.okbutton = self.findChild(QtWidgets.QPushButton, 'btn_OK') 43 | self.okbutton.clicked.connect(self.OKButtonClicked) 44 | 45 | self.cancelbutton = self.findChild(QtWidgets.QPushButton, 'btn_cancel') 46 | self.cancelbutton.clicked.connect(self.CancelButtonClicked) 47 | 48 | self.selectencfsbutton = self.findChild(QtWidgets.QPushButton, 'btn_selectencfs') 49 | self.selectencfsbutton.clicked.connect(self.SelectEncfsButtonClicked) 50 | 51 | self.selectgocryptfsbutton = self.findChild(QtWidgets.QPushButton, 'btn_selectgocryptfs') 52 | self.selectgocryptfsbutton.clicked.connect(self.SelectGocryptfsButtonClicked) 53 | 54 | self.selectmountbutton = self.findChild(QtWidgets.QPushButton, 'btn_selectmount') 55 | self.selectmountbutton.clicked.connect(self.SelectMountButtonClicked) 56 | 57 | self.selectumountbutton = self.findChild(QtWidgets.QPushButton, 'btn_selectumount') 58 | self.selectumountbutton.clicked.connect(self.SelectUmountButtonClicked) 59 | 60 | self.selectworkingfolderbutton = self.findChild(QtWidgets.QPushButton, 'btn_selectworkingfolder') 61 | self.selectworkingfolderbutton.clicked.connect(self.SelectWorkingFolderClicked) 62 | 63 | self.settingsfile = encfsgui_globals.settingsfile 64 | 65 | 66 | def SelectEncfsButtonClicked(self): 67 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 68 | self.txt_enfcsbinary = self.findChild(QtWidgets.QLineEdit, 'txt_encfsbinary') 69 | options = QtWidgets.QFileDialog.Options() 70 | options |= QtWidgets.QFileDialog.DontUseNativeDialog 71 | fileName, _ = QtWidgets.QFileDialog.getOpenFileName(self,"Select 'encfs' binary", self.txt_enfcsbinary.displayText(),"All Files (*)", options=options) 72 | if fileName: 73 | self.txt_encfsbinary.setText("%s" % fileName) 74 | return 75 | 76 | def SelectGocryptfsButtonClicked(self): 77 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 78 | self.txt_gocryptfsbinary = self.findChild(QtWidgets.QLineEdit, 'txt_gocryptfsbinary') 79 | options = QtWidgets.QFileDialog.Options() 80 | options |= QtWidgets.QFileDialog.DontUseNativeDialog 81 | fileName, _ = QtWidgets.QFileDialog.getOpenFileName(self,"Select 'gocryptfs' binary", self.txt_gocryptfsbinary.displayText(),"All Files (*)", options=options) 82 | if fileName: 83 | self.txt_gocryptfsbinary.setText("%s" % fileName) 84 | return 85 | 86 | def SelectMountButtonClicked(self): 87 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 88 | self.txt_mountbinary = self.findChild(QtWidgets.QLineEdit, 'txt_mountbinary') 89 | options = QtWidgets.QFileDialog.Options() 90 | options |= QtWidgets.QFileDialog.DontUseNativeDialog 91 | fileName, _ = QtWidgets.QFileDialog.getOpenFileName(self,"Select 'mount' binary", self.txt_mountbinary.displayText(),"All Files (*)", options=options) 92 | if fileName: 93 | self.txt_mountbinary.setText("%s" % fileName) 94 | return 95 | 96 | def SelectUmountButtonClicked(self): 97 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 98 | self.txt_umountbinary = self.findChild(QtWidgets.QLineEdit, 'txt_umountbinary') 99 | options = QtWidgets.QFileDialog.Options() 100 | options |= QtWidgets.QFileDialog.DontUseNativeDialog 101 | fileName, _ = QtWidgets.QFileDialog.getOpenFileName(self,"Select 'umount' binary", self.txt_umountbinary.displayText(),"All Files (*)", options=options) 102 | if fileName: 103 | self.txt_umountbinary.setText("%s" % fileName) 104 | return 105 | 106 | def SelectWorkingFolderClicked(self): 107 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 108 | self.txt_workingfolder = self.findChild(QtWidgets.QLineEdit, 'txt_workingfolder') 109 | options = QtWidgets.QFileDialog.Options() 110 | options |= QtWidgets.QFileDialog.DontUseNativeDialog 111 | folderName = str(QtWidgets.QFileDialog.getExistingDirectory(self, "Select Directory")) 112 | if folderName: 113 | self.txt_workingfolder.setText("%s" % folderName) 114 | return 115 | 116 | def OKButtonClicked(self): 117 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 118 | self.saveSettings() 119 | self.close() 120 | return 121 | 122 | def CancelButtonClicked(self): 123 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 124 | self.close() 125 | return 126 | 127 | def loadSettings(self): 128 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 129 | # populate fields 130 | self.txt_enfcsbinary = self.findChild(QtWidgets.QLineEdit, 'txt_encfsbinary') 131 | self.txt_enfcsbinary.setText("%s" % encfsgui_globals.g_Settings["encfspath"]) 132 | self.txt_gocryptfsbinary = self.findChild(QtWidgets.QLineEdit, 'txt_gocryptfsbinary') 133 | self.txt_gocryptfsbinary.setText("%s" % encfsgui_globals.g_Settings["gocryptfspath"]) 134 | self.txt_mountbinary = self.findChild(QtWidgets.QLineEdit, 'txt_mountbinary') 135 | self.txt_mountbinary.setText("%s" % encfsgui_globals.g_Settings["mountpath"]) 136 | self.txt_umountbinary = self.findChild(QtWidgets.QLineEdit, 'txt_umountbinary') 137 | self.txt_umountbinary.setText("%s" % encfsgui_globals.g_Settings["umountpath"]) 138 | self.txt_workingfolder = self.findChild(QtWidgets.QLineEdit, 'txt_workingfolder') 139 | self.txt_workingfolder.setText("%s" % encfsgui_globals.g_Settings["workingfolder"]) 140 | self.chk_starthidden = self.findChild(QtWidgets.QCheckBox, 'chk_starthidden') 141 | self.chk_autounmount = self.findChild(QtWidgets.QCheckBox, 'chk_autounmount') 142 | self.chk_autoupdate = self.findChild(QtWidgets.QCheckBox, 'chk_autoupdate') 143 | self.chk_noconfirmationunmount = self.findChild(QtWidgets.QCheckBox, 'chk_noconfirmationunmount') 144 | self.chk_noconfirmationexit = self.findChild(QtWidgets.QCheckBox, 'chk_noconfirmationexit') 145 | self.chk_debugmode = self.findChild(QtWidgets.QCheckBox, 'chk_debugmode') 146 | self.chk_confirmforceunmountall = self.findChild(QtWidgets.QCheckBox, 'chk_confirmforceunmountall') 147 | self.chk_doubleclickmount = self.findChild(QtWidgets.QCheckBox, 'chk_doubleclickmount') 148 | self.chk_encrypt = self.findChild(QtWidgets.QCheckBox, 'chk_encrypt') 149 | self.chk_clearkeywhenhidden = self.findChild(QtWidgets.QCheckBox, 'chk_clearkeywhenhidden') 150 | self.chk_hidevolumenotfound = self.findChild(QtWidgets.QCheckBox, 'chk_hidevolumenotfound') 151 | 152 | if (encfsgui_globals.g_Settings["autounmount"].lower() == "true"): 153 | self.chk_autounmount.setChecked(True) 154 | 155 | if (encfsgui_globals.g_Settings["starthidden"].lower() == "true"): 156 | self.chk_starthidden.setChecked(True) 157 | 158 | if (encfsgui_globals.g_Settings["noconfirmationunmount"].lower() == "true"): 159 | self.chk_noconfirmationunmount.setChecked(True) 160 | else: 161 | self.chk_noconfirmationunmount.setChecked(False) 162 | 163 | if (encfsgui_globals.g_Settings["noconfirmationexit"].lower() == "true"): 164 | self.chk_noconfirmationexit.setChecked(True) 165 | 166 | if (encfsgui_globals.g_Settings["debugmode"].lower() == "true"): 167 | self.chk_debugmode.setChecked(True) 168 | 169 | if (encfsgui_globals.g_Settings["autoupdate"].lower() == "true"): 170 | self.chk_autoupdate.setChecked(True) 171 | 172 | if (encfsgui_globals.g_Settings["confirmforceunmountall"].lower() == "true"): 173 | self.chk_confirmforceunmountall.setChecked(True) 174 | 175 | if (encfsgui_globals.g_Settings["doubleclickmount"].lower() == "true"): 176 | self.chk_doubleclickmount.setChecked(True) 177 | 178 | if (encfsgui_globals.g_Settings["encrypt"].lower() == "true"): 179 | self.chk_encrypt.setChecked(True) 180 | 181 | if (encfsgui_globals.g_Settings["clearkeywhenhidden"].lower() == "true"): 182 | self.chk_clearkeywhenhidden.setChecked(True) 183 | 184 | if (encfsgui_globals.g_Settings["hidevolumenotfound"].lower() == "true"): 185 | self.chk_hidevolumenotfound.setChecked(True) 186 | 187 | return 188 | 189 | 190 | def saveSettings(self): 191 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 192 | # update global settings 193 | encfsgui_globals.g_Settings["encfspath"] = str(self.txt_enfcsbinary.displayText().strip()) 194 | encfsgui_globals.g_Settings["gocryptfspath"] = str(self.txt_gocryptfsbinary.displayText().strip()) 195 | encfsgui_globals.g_Settings["mountpath"] = str(self.txt_mountbinary.displayText().strip()) 196 | encfsgui_globals.g_Settings["umountpath"] = str(self.txt_umountbinary.displayText().strip()) 197 | if (self.txt_workingfolder.displayText().strip().replace(" ","") == ""): 198 | self.txt_workingfolder.setText(".") 199 | encfsgui_globals.g_Settings["workingfolder"] = str(self.txt_workingfolder.displayText().strip()) 200 | encfsgui_globals.g_Settings["autounmount"] = str(self.chk_autounmount.isChecked()).lower() 201 | encfsgui_globals.g_Settings["noconfirmationunmount"] = str(self.chk_noconfirmationunmount.isChecked()).lower() 202 | encfsgui_globals.g_Settings["noconfirmationexit"] = str(self.chk_noconfirmationexit.isChecked()).lower() 203 | encfsgui_globals.g_Settings["starthidden"] = str(self.chk_starthidden.isChecked()).lower() 204 | encfsgui_globals.g_Settings["debugmode"] = str(self.chk_debugmode.isChecked()).lower() 205 | encfsgui_globals.g_Settings["autoupdate"] = str(self.chk_autoupdate.isChecked()).lower() 206 | encfsgui_globals.g_Settings["confirmforceunmountall"] = str(self.chk_confirmforceunmountall.isChecked()).lower() 207 | encfsgui_globals.g_Settings["doubleclickmount"] = str(self.chk_doubleclickmount.isChecked()).lower() 208 | encfsgui_globals.g_Settings["clearkeywhenhidden"] = str(self.chk_clearkeywhenhidden.isChecked()).lower() 209 | encfsgui_globals.g_Settings["hidevolumenotfound"] = str(self.chk_hidevolumenotfound.isChecked()).lower() 210 | if self.chk_debugmode.isChecked(): 211 | encfsgui_globals.debugmode = True 212 | encfsgui_helper.print_debug("Enable debug mode") 213 | else: 214 | encfsgui_helper.print_debug("Disable debug mode") 215 | encfsgui_globals.debugmode = False 216 | 217 | # encryption? 218 | if (self.chk_encrypt.isChecked() and str(encfsgui_globals.g_Settings["encrypt"]).lower() == "false"): 219 | # ask for master password 220 | encryptionpassword = "" 221 | msgBox = QtWidgets.QMessageBox() 222 | msgBox.setWindowTitle("Enable encryption/master password?") 223 | msgBox.setText("You are about to enable encryption of the encfsgui.volumes file.\n\nYour 'masterkey' password will NOT be stored and cannot be recovered.\nIf you forget the master password, you will not be able to recover/decrypt the contents of the encfsgui.volumes file.\n\nAre you sure?") 224 | msgBox.setIcon(QMessageBox.Question) 225 | msgBox.setStandardButtons(QtWidgets.QMessageBox.Yes) 226 | msgBox.addButton(QtWidgets.QMessageBox.No) 227 | msgBox.show() 228 | if (msgBox.exec_() == QtWidgets.QMessageBox.Yes): 229 | # ask for the password 230 | masterpwwindow = CNewMasterKeyWindow() 231 | masterpwwindow.setWindowTitle("Please enter master password") 232 | masterpwwindow.show() 233 | masterpwwindow.exec_() 234 | encryptionpassword = masterpwwindow.getPassword() 235 | # no password = no encryption 236 | if encryptionpassword != "": 237 | encfsgui_globals.masterkey = encryptionpassword 238 | encfsgui_globals.g_Settings["encrypt"] = "true" 239 | # make sure to encrypt the volumes file - simply request volumes file to be written to disk 240 | # encryption is handled by the saveVolumes method 241 | encfsgui_globals.appconfig.saveVolumes() 242 | QtWidgets.QMessageBox.information(None,"Encryption enabled","Encryption enabled.") 243 | else: 244 | encfsgui_globals.g_Settings["encrypt"] = "false" 245 | 246 | removeencryption = False 247 | if (not self.chk_encrypt.isChecked()) and str(encfsgui_globals.g_Settings["encrypt"]).lower() == "true": 248 | # ask to confirm to remove encryption? 249 | msgBox = QtWidgets.QMessageBox() 250 | msgBox.setWindowTitle("Disable encryption/master password?") 251 | msgBox.setText("You are about to disable encryption of the encfsgui.volumes file.\n\nThis will decrypt the contents of the file and remove the need to use a master password.\n\nAre you sure?") 252 | msgBox.setIcon(QMessageBox.Question) 253 | msgBox.setStandardButtons(QtWidgets.QMessageBox.No) 254 | msgBox.addButton(QtWidgets.QMessageBox.Yes) 255 | msgBox.show() 256 | if (msgBox.exec_() == QtWidgets.QMessageBox.Yes): 257 | removeencryption = True 258 | if removeencryption: 259 | encfsgui_globals.g_Settings["encrypt"] = "false" 260 | encfsgui_globals.masterkey = "" 261 | else: 262 | # leave encryption active 263 | encfsgui_globals.g_Settings["encrypt"] = "true" 264 | 265 | # and write to file 266 | config = configparser.RawConfigParser() 267 | config.add_section('config') 268 | for settingkey in encfsgui_globals.g_Settings: 269 | config.set('config', settingkey, encfsgui_globals.g_Settings[settingkey]) 270 | 271 | config.add_section('encodings') 272 | config.set('encodings','filenameencodings', ",".join(encfsgui_globals.g_Encodings)) 273 | 274 | # save file to disk 275 | with open(encfsgui_globals.settingsfile, 'w') as configfile: 276 | config.write(configfile) 277 | 278 | # save volumes (to make sure it gets encrypted or decrypted) 279 | encfsgui_globals.appconfig.saveVolumes() 280 | 281 | if removeencryption: 282 | QtWidgets.QMessageBox.information(None,"Encryption disabled","Encryption disabled.") 283 | return 284 | -------------------------------------------------------------------------------- /cconfig.py: -------------------------------------------------------------------------------- 1 | # encfsgui CConfig 2 | # 3 | # 4 | 5 | import os 6 | import sys 7 | import time 8 | import datetime 9 | import string 10 | import configparser 11 | import inspect 12 | 13 | from PyQt5 import uic 14 | from PyQt5 import QtWidgets 15 | from PyQt5.QtWidgets import * 16 | from PyQt5 import QtCore 17 | 18 | import encfsgui_globals 19 | from encfsgui_globals import * 20 | 21 | import encfsgui_helper 22 | from encfsgui_helper import * 23 | 24 | 25 | class CConfig(): 26 | def __init__(self): 27 | self.settingsfile = encfsgui_globals.settingsfile 28 | return 29 | 30 | def getVolumes(self): 31 | curframe = inspect.currentframe() 32 | calframe = inspect.getouterframes(curframe, 2) 33 | encfsgui_helper.print_debug("%s() Called from: %s()" % (inspect.stack()[0][3],calframe[1][3])) 34 | encfsgui_globals.g_Volumes.clear() 35 | 36 | volumeconfig = configparser.ConfigParser() 37 | volumeconfig.read(encfsgui_globals.volumesfile) 38 | 39 | # prepare to get mount states 40 | mountcmd = encfsgui_globals.g_Settings["mountpath"] 41 | mountlist = encfsgui_helper.execOSCmd(mountcmd) 42 | 43 | for volumename in volumeconfig.sections(): 44 | EncVolume = CEncryptedVolume() 45 | 46 | EncVolume.errormessage = "" 47 | 48 | if "enc_path" in volumeconfig[volumename]: 49 | EncVolume.enc_path = volumeconfig[volumename]["enc_path"] 50 | if "mount_path" in volumeconfig[volumename]: 51 | EncVolume.mount_path = volumeconfig[volumename]["mount_path"] 52 | if "automount" in volumeconfig[volumename]: 53 | EncVolume.automount = volumeconfig[volumename]["automount"] 54 | if "preventautounmount" in volumeconfig[volumename]: 55 | EncVolume.preventautounmount = volumeconfig[volumename]["preventautounmount"] 56 | if "allowother" in volumeconfig[volumename]: 57 | EncVolume.allowother = volumeconfig[volumename]["allowother"] 58 | if "mountaslocal" in volumeconfig[volumename]: 59 | EncVolume.mountaslocal = volumeconfig[volumename]["mountaslocal"] 60 | if "encfsmountoptions" in volumeconfig[volumename]: 61 | EncVolume.encfsmountoptions = volumeconfig[volumename]["encfsmountoptions"] 62 | if "passwordsaved" in volumeconfig[volumename]: 63 | EncVolume.passwordsaved = volumeconfig[volumename]["passwordsaved"] 64 | if "type" in volumeconfig[volumename]: 65 | EncVolume.enctype = volumeconfig[volumename]["type"] 66 | if "enctype" in volumeconfig[volumename]: 67 | EncVolume.enctype = volumeconfig[volumename]["enctype"] 68 | else: 69 | EncVolume.enctype = "encfs" 70 | if "sudo" in volumeconfig[volumename]: 71 | EncVolume.sudo = volumeconfig[volumename]["sudo"] 72 | 73 | # do we need to decrypt ? 74 | # if encryption is enabled, decrypt strings in memory using master password 75 | if encfsgui_globals.g_Settings["encrypt"] == "true": 76 | # do we have the master key? 77 | if (encfsgui_globals.masterkey == ""): 78 | # ask for masterkey 79 | encfsgui_helper.getMasterKey() 80 | print_debug("Obtained masterkey, length %d" % len(encfsgui_globals.masterkey)) 81 | if (encfsgui_globals.masterkey != ""): 82 | try: 83 | EncVolume.enc_path = encfsgui_helper.decrypt(EncVolume.enc_path).decode() 84 | EncVolume.mount_path = encfsgui_helper.decrypt(EncVolume.mount_path).decode() 85 | encfsgui_globals.timeswrong = 0 86 | except Exception: 87 | msg = traceback.format_exc() 88 | print(msg) 89 | encfsgui_helper.print_debug(msg) 90 | QtWidgets.QMessageBox.critical(None,"Error","Error decrypting information.\n\nTry again later.") 91 | encfsgui_globals.masterkey = "" 92 | encfsgui_globals.timeswrong += 1 93 | if encfsgui_globals.timeswrong > 3: 94 | sys.exit(1) 95 | 96 | encfsgui_helper.print_debug("Check if path '%s' exists" % EncVolume.enc_path) 97 | EncVolume.enc_path_exists = os.path.exists(EncVolume.enc_path) 98 | encfsgui_helper.print_debug(">> %s" % str(EncVolume.enc_path_exists)) 99 | if not EncVolume.enc_path_exists: 100 | EncVolume.errormessage += "- Encrypted folder '%s' does not seem to exist\n" % EncVolume.enc_path 101 | EncVolume.ismounted = False 102 | encfsgui_helper.print_debug("Check if %s volume '%s' is mounted at '%s'" % (EncVolume.enctype, volumename, EncVolume.mount_path)) 103 | if not os.path.exists(EncVolume.mount_path): 104 | encfsgui_helper.print_debug("Warning! '%s' does not seem to exist!" % (EncVolume.mount_path)) 105 | EncVolume.errormessage += "- Mount path '%s' does not seem to exist\n" % EncVolume.mount_path 106 | if EncVolume.mount_path != "": 107 | # the extra space is important ! 108 | path_to_check = "%s " % EncVolume.mount_path 109 | for item in mountlist: 110 | thismountline = str(item).strip() 111 | foundinitem = False 112 | if path_to_check in thismountline: 113 | foundinitem = True 114 | encfsgui_helper.print_debug("Mount check: Does '%s' contain '%s'? %s" % (thismountline, path_to_check, str(foundinitem))) 115 | if EncVolume.enctype == "encfs": 116 | if path_to_check in thismountline: 117 | encfsgui_helper.print_debug("EncFS volume is mounted, mount path '%s' found in '%s'" % (path_to_check, thismountline)) 118 | EncVolume.ismounted = True 119 | break 120 | elif EncVolume.enctype == "gocryptfs": 121 | if path_to_check in thismountline: 122 | encfsgui_helper.print_debug("GoCryptFS volume is mounted, mount path '%s' found in '%s'" % (path_to_check, thismountline)) 123 | EncVolume.ismounted = True 124 | break 125 | if not EncVolume.ismounted: 126 | encfsgui_helper.print_debug("Volume is not mounted") 127 | else: 128 | EncVolume.errormessage = "" 129 | encfsgui_globals.g_Volumes[volumename] = EncVolume 130 | 131 | self.clearMasterKeyIfNeeded() 132 | return 133 | 134 | 135 | def clearMasterKeyIfNeeded(self): 136 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 137 | curframe = inspect.currentframe() 138 | calframe = inspect.getouterframes(curframe, 2) 139 | encfsgui_helper.print_debug("%s() Called from: %s()" % (inspect.stack()[0][3],calframe[1][3])) 140 | if encfsgui_globals.ishidden: 141 | if encfsgui_globals.g_Settings["clearkeywhenhidden"].lower() == "true": 142 | encfsgui_globals.masterkey = "" 143 | encfsgui_helper.print_debug("Cleared masterkey") 144 | return 145 | 146 | def getSettings(self): 147 | settingsfound = True 148 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 149 | curframe = inspect.currentframe() 150 | calframe = inspect.getouterframes(curframe, 2) 151 | encfsgui_helper.print_debug("%s() Called from: %s()" % (inspect.stack()[0][3],calframe[1][3])) 152 | # if file does not exist, generate default file 153 | if not os.path.exists(self.settingsfile): 154 | self.populateDefaultSettings() 155 | self.saveSettings() 156 | settingsfound = False 157 | 158 | # if file exists, but does not contain the config section, then regenerate the file as well 159 | appsettings = configparser.ConfigParser() 160 | appsettings.read(self.settingsfile) 161 | 162 | if not "config" in appsettings: 163 | self.populateDefaultSettings() 164 | self.saveSettings() 165 | settingsfound = False 166 | 167 | # we should have a working config file now 168 | appsettings = configparser.ConfigParser() 169 | appsettings.read(self.settingsfile) 170 | 171 | for settingkey in appsettings["config"]: 172 | encfsgui_globals.g_Settings[settingkey] = appsettings["config"][settingkey].strip().replace("\n","") 173 | 174 | if "encodings" in appsettings: 175 | encodinglist = appsettings["encodings"]["filenameencodings"] 176 | encfsgui_globals.g_Encodings = encodinglist.split(",") 177 | 178 | # in case the current settings file is incomplete, pick up additional settings 179 | self.populateDefaultSettings() 180 | self.saveSettings() 181 | 182 | encfsgui_globals.volumesfile = encfsgui_globals.g_Settings["workingfolder"] + "/" + 'encfsgui.volumes' 183 | 184 | return settingsfound 185 | 186 | def populateDefaultSettings(self): 187 | #global encfsgui_globals.g_Settings 188 | if not "encfspath" in encfsgui_globals.g_Settings: 189 | whichpath = encfsgui_helper.runwhich("encfs") 190 | if whichpath != "": 191 | encfsgui_globals.g_Settings["encfspath"] = whichpath 192 | else: 193 | encfsgui_globals.g_Settings["encfspath"] = "/usr/local/bin/encfs" 194 | 195 | if not "gocryptfspath" in encfsgui_globals.g_Settings: 196 | whichpath = encfsgui_helper.runwhich("gocryptfs") 197 | if whichpath != "": 198 | encfsgui_globals.g_Settings["gocryptfspath"] = whichpath 199 | else: 200 | encfsgui_globals.g_Settings["gocryptfspath"] = "/usr/local/bin/gocryptfs" 201 | if os.path.exists("/opt/homebrew/bin/gocryptfs"): 202 | encfsgui_globals.g_Settings["gocryptfspath"] = "/opt/homebrew/bin/gocryptfs" 203 | 204 | if not "mountpath" in encfsgui_globals.g_Settings: 205 | whichpath = encfsgui_helper.runwhich("mount") 206 | if whichpath != "": 207 | encfsgui_globals.g_Settings["mountpath"] = whichpath 208 | else: 209 | encfsgui_globals.g_Settings["mountpath"] = "/sbin/mount" 210 | 211 | if not "umountpath" in encfsgui_globals.g_Settings: 212 | whichpath = encfsgui_helper.runwhich("umount") 213 | if whichpath != "": 214 | encfsgui_globals.g_Settings["umountpath"] = whichpath 215 | else: 216 | encfsgui_globals.g_Settings["umountpath"] = "/sbin/umount" 217 | 218 | if not "autounmount" in encfsgui_globals.g_Settings: 219 | encfsgui_globals.g_Settings["autounmount"] = "false" 220 | if not "noconfirmationunmount" in encfsgui_globals.g_Settings: 221 | encfsgui_globals.g_Settings["noconfirmationunmount"] = "false" 222 | if not "noconfirmationexit" in encfsgui_globals.g_Settings: 223 | encfsgui_globals.g_Settings["noconfirmationexit"] = "false" 224 | if not "workingfolder" in encfsgui_globals.g_Settings: 225 | encfsgui_globals.g_Settings["workingfolder"] = "." 226 | if not "starthidden" in encfsgui_globals.g_Settings: 227 | encfsgui_globals.g_Settings["starthidden"] = "false" 228 | if not "debugmode" in encfsgui_globals.g_Settings: 229 | encfsgui_globals.g_Settings["debugmode"] = "false" 230 | encfsgui_globals.debugmode = False 231 | if not "autoupdate" in encfsgui_globals.g_Settings: 232 | encfsgui_globals.g_Settings["autoupdate"] = "false" 233 | if not "confirmforceunmountall" in encfsgui_globals.g_Settings: 234 | encfsgui_globals.g_Settings["confirmforceunmountall"] = "false" 235 | if not "doubleclickmount" in encfsgui_globals.g_Settings: 236 | encfsgui_globals.g_Settings["doubleclickmount"] = "false" 237 | if not "encrypt" in encfsgui_globals.g_Settings: 238 | encfsgui_globals.g_Settings["encrypt"] = "false" 239 | if not "clearkeywhenhidden" in encfsgui_globals.g_Settings: 240 | encfsgui_globals.g_Settings["clearkeywhenhidden"] = "false" 241 | if not "hidevolumenotfound" in encfsgui_globals.g_Settings: 242 | encfsgui_globals.g_Settings["hidevolumenotfound"] = "false" 243 | return 244 | 245 | def addVolume(self, volumename, EncVolumeObj): 246 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 247 | encfsgui_globals.g_Volumes[volumename] = EncVolumeObj 248 | # write g_Volumes to volumes file 249 | self.saveVolumes() 250 | return 251 | 252 | def delVolume(self, volumename): 253 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 254 | # remove volume from g_Volumes 255 | if (volumename in encfsgui_globals.g_Volumes): 256 | del encfsgui_globals.g_Volumes[volumename] 257 | # write g_Volumes to volumes file 258 | self.saveVolumes() 259 | return 260 | 261 | def saveSettings(self): 262 | # just in case a new setting was added, get default values 263 | self.populateDefaultSettings() 264 | config = configparser.RawConfigParser() 265 | config.add_section('config') 266 | for settingkey in encfsgui_globals.g_Settings: 267 | config.set('config', settingkey, encfsgui_globals.g_Settings[settingkey]) 268 | 269 | if len(encfsgui_globals.g_Encodings) > 0: 270 | config.add_section('encodings') 271 | config.set('encodings','filenameencodings', ",".join(encfsgui_globals.g_Encodings)) 272 | 273 | # save file to disk 274 | with open(self.settingsfile, 'w') as configfile: 275 | config.write(configfile) 276 | 277 | encfsgui_globals.volumesfile = encfsgui_globals.g_Settings["workingfolder"] + "/" + 'encfsgui.volumes' 278 | 279 | return 280 | 281 | def saveVolumes(self): 282 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 283 | config = configparser.RawConfigParser() 284 | for volumename in encfsgui_globals.g_Volumes: 285 | EncVolumeObj = encfsgui_globals.g_Volumes[volumename] 286 | config.add_section(volumename) 287 | str_enc_path = EncVolumeObj.enc_path 288 | str_mount_path = EncVolumeObj.mount_path 289 | # if encryption is enabled, encrypt the strings first using the master password 290 | if encfsgui_globals.g_Settings["encrypt"] == "true": 291 | if encfsgui_globals.masterkey == "": 292 | # ask for masterkey 293 | encfsgui_helper.getMasterKey() 294 | if len(encfsgui_globals.masterkey) == 32: 295 | str_enc_path = encfsgui_helper.encrypt(EncVolumeObj.enc_path) 296 | str_mount_path = encfsgui_helper.encrypt(EncVolumeObj.mount_path) 297 | config.set(volumename, 'enc_path' , str_enc_path) 298 | config.set(volumename, 'mount_path', str_mount_path) 299 | config.set(volumename, 'automount', EncVolumeObj.automount) 300 | config.set(volumename, 'preventautounmount', EncVolumeObj.preventautounmount) 301 | config.set(volumename, 'allowother', EncVolumeObj.allowother) 302 | config.set(volumename, 'mountaslocal', EncVolumeObj.mountaslocal) 303 | config.set(volumename, 'encfsmountoptions', EncVolumeObj.encfsmountoptions) 304 | config.set(volumename, 'passwordsaved', EncVolumeObj.passwordsaved) 305 | config.set(volumename, 'enctype', EncVolumeObj.enctype) 306 | config.set(volumename, 'sudo', EncVolumeObj.sudo) 307 | 308 | with open(encfsgui_globals.volumesfile, 'w') as configfile: 309 | config.write(configfile) 310 | 311 | self.clearMasterKeyIfNeeded() 312 | 313 | return 314 | -------------------------------------------------------------------------------- /bitmaps/mountfolder.xpm: -------------------------------------------------------------------------------- 1 | /* XPM */ 2 | static const char *mountfolder[] = { 3 | /* columns rows colors chars-per-pixel */ 4 | "128 128 91 1 ", 5 | " c #393C43", 6 | ". c #3C4047", 7 | "X c #3F424B", 8 | "o c #42454D", 9 | "O c #464953", 10 | "+ c #494C56", 11 | "@ c #4B4F59", 12 | "# c #4E525C", 13 | "$ c #50545F", 14 | "% c #535762", 15 | "& c #535963", 16 | "* c #5A5E67", 17 | "= c #575C68", 18 | "- c #5A5E69", 19 | "; c #5D626D", 20 | ": c #5F6570", 21 | "> c #616671", 22 | ", c #646A75", 23 | "< c #666D79", 24 | "1 c #686F7B", 25 | "2 c #6A717D", 26 | "3 c #DF6345", 27 | "4 c #DF664A", 28 | "5 c #DF694E", 29 | "6 c #DE6D53", 30 | "7 c #DD735B", 31 | "8 c #E06244", 32 | "9 c #DD7761", 33 | "0 c #DC7A66", 34 | "q c #DC7D6A", 35 | "w c #DB816E", 36 | "e c #DB8573", 37 | "r c #DA8B7C", 38 | "t c #6D7582", 39 | "y c #707682", 40 | "u c #727986", 41 | "i c #747C89", 42 | "p c #7A7F8A", 43 | "a c #7B818E", 44 | "s c #7D8592", 45 | "d c #818692", 46 | "f c #808895", 47 | "g c #858D9A", 48 | "h c #898E9A", 49 | "j c #8A919D", 50 | "k c #8D95A2", 51 | "l c #9196A3", 52 | "z c #939AA7", 53 | "x c #989DA7", 54 | "c c #959DAA", 55 | "v c #999FAB", 56 | "b c #9AA1AD", 57 | "n c #9FA6B3", 58 | "m c #A0A6B3", 59 | "M c #A3A9B5", 60 | "N c #A5ACB9", 61 | "B c #A8AFBC", 62 | "V c #ABB1BE", 63 | "C c #DA8E80", 64 | "Z c #D99185", 65 | "A c #D9958A", 66 | "S c #D8988F", 67 | "D c #D89C93", 68 | "F c #D7A39D", 69 | "G c #D7A6A1", 70 | "H c #D7A8A4", 71 | "J c #D6ADAA", 72 | "K c #D5B3B3", 73 | "L c #D5B7B9", 74 | "P c #D4BABC", 75 | "I c #ADB4C1", 76 | "U c #B1B7C3", 77 | "Y c #B4B9C6", 78 | "T c #B6BCC9", 79 | "R c #B8BECB", 80 | "E c #D4BDC1", 81 | "W c #BCC2CF", 82 | "Q c #BEC4D1", 83 | "! c #D3C0C5", 84 | "~ c #D3C5CB", 85 | "^ c #C1C7D4", 86 | "/ c #C4CAD7", 87 | "( c #C6CCD9", 88 | ") c #CACFDB", 89 | "_ c #D2CAD3", 90 | "` c #D1CFD9", 91 | "' c #CBD1DE", 92 | "] c #D1D1DD", 93 | "[ c #CED4E1", 94 | "{ c #D1D7E4", 95 | "} c None", 96 | /* pixels */ 97 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 98 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 99 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 100 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 101 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 102 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 103 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 104 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 105 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 106 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 107 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 108 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 109 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 110 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 111 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 112 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 113 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 114 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 115 | "}}}}}}}}}}} . . .}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 116 | "}}}}}}}}}} o@$@@#@##@##@###@#@@#@@@@$@#@@@@%@@@@@o }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 117 | "}}}}}}}}} @$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%$$$$$$%$@ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 118 | "}}}}}}}} o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$X }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 119 | "}}}}}}}} +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 120 | "}}}}}}}} +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$@.}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 121 | "}}}}}}}} +$$$$$$$$$$$$#%$$$$$$$$$$$$$$$$$$$$$$$$$$$@ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 122 | "}}}}}}}} +$$$$$$$$$$$$$#$$$$$$$$$$$$$$$$$$$$$$$$$$$+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 123 | "}}}}}}}} @$$$$$$$$$$$$#%$$$$$$$$$$$$$$$$$$$$$$$$$$$@ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 124 | "}}}}}}}} +$$$$$$$$$$$$%#$$$$$$$$$$$$$$$$$$$$$$$$$$$# }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 125 | "}}}}}}}} +$$$#%#$$$$%##%$$$$$$$$$$$$$$$$$$$$$$$$$$$$#o..XXXXXXXXoo.o o ooX oXXXXXXXXXXXXXX.XXX. }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 126 | "}}}}}}}} +$$$%#%$$$$#%%#$$$$$$$$$$$$$$$$$$$$$%$$$$$$%$%%$$$$$$$&$%$$%%$$$$$%$%$$$%$$$$$$$%$$$%$$o }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 127 | "}}}}}}}} @$$==================**================================================================= }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 128 | "}}}}}}}} +$$unnnnnnnmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnng:+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 129 | "}}}}}}}} @$$i{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{[{[{{{{{[{[{[{{{[{[{{{n:+o}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 130 | "}}}}}}}} +$$i[{{Qzzzzzzczzzzzczzzzzzcczzzzzzzzzzzzzzczzzzzczzczzzzzzzzzcczcczzzcczczczccczczczcf:+@}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 131 | "}}}}}}}} +$$i{{{InQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQTTQ[}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 132 | "}}}}}}}} @$$i{{{nT{{{///////Q[Q[(Q[Q(Q([Q(((((Q(((Q[Q((/Q((Q(Q[Q////////////////////////////////////}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 133 | "}}}}}}}} +$$i{{{nR{{{igssssksssssssssksssssssssssgsssgsssksssssksfffffffsfsgffffsfffgsgssfsgdddfsgsk}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 134 | "}}}}}}}} +$$i{{{cQ{{/s{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{'ImmnmmmnnmmmmmmmmmmnnmmnnmmmmnmmMmmmmk}}}}}}}}}}}}}}}}}}}}}}}", 135 | "}}}}}}}} @$$i{{{k/{{Tz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{Ritttttttttttttttttttttttttttuutttttttttt2}}}}}}}}}}}}}}}}}}}}}", 136 | "}}}}}}}} +$$i{{{k[{{nn{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{Qitt>-;-------;-------------***-----------;}}}}}}}}}}}}}}}}}}}}}", 137 | "}}}}}}}} @$$i{{{g[{{cT{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{[Witt=----------------------------------------}}}}}}}}}}}}}}}}}}}}", 138 | "}}}}}}}} +$$i{{{g[{{gQ{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{Qitt------------------------------------------}}}}}}}}}}}}}}}}}}}}", 139 | "}}}}}}}} +$$i{{{g[{{i[{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{Tit<-------------------------------------------}}}}}}}}}}}}}}}}}}}}", 140 | "}}}}}}}} @$$i{[[k{{/s[{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{Uit2-------------------------------------------}}}}}}}}}}}}}}}}}}}}}", 141 | "}}}}}}}} +$$i{{/k[{Tk{)Mccckczzkzzzzllzzzzzknzzzlcllclzzclzstt2--------------------------------------------}}}}}}}}}}}}}}}}}}}}}", 142 | "}}}}}}}} +$$i{[Qn{{In/itttttttttttttttttttttattttttttttttttt2,---------------------------------------------}}}}}}}}}}}}}}}}}}}}}", 143 | "}}}}}}}} +$$i{{Tn[[nIgtt<-=-----===;=====;=;;===----==----=;-----------------------------------------------}}}}}}}}}}}}}}}}}}}}}", 144 | "}}}}}}}} +$$i{{II{[hItt,=-------;;==;=;;==;=;;==-----------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}", 145 | "}}}}}}}} @$$i{{NU{{aItt=--------=;-------------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}", 146 | "}}}}}}}} +$$i{{nT{[ictt=--------==-------------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}", 147 | "}}}}}}}} +$$i{{cQ{Qkgi:=--------;**-----------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}}", 148 | "}}}}}}}} +$$i{{k({Ibit:=--------=**-----------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}}", 149 | "}}}}}}}} @$$i{{g[{nntt;=----------------------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}}", 150 | "}}}}}}}} +$$i{{k{[kntt==----------------------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}}", 151 | "}}}}}}}} +$$i{{s{{snt<;=----------------------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}}", 152 | "}}}}}}}} +$$i{{s{[izt<------------------------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}}", 153 | "}}}}}}}} @$$i{[k{Qgst>-----------------------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}}}", 154 | "}}}}}}}} @$$i{Qz{Tctt------------------------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}}}", 155 | "}}}}}}}} +$$i{Qb{nctt=-----------------------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}}}", 156 | "}}}}}}}} +$$i{Tn{cktt------------------------------------------------------------------------------------}}}}}}}}}}}}}}}}}}}}}}}", 157 | "}}}}}}}} +$$i{In{gzt<-----------------------------------------------------------------------------**-----}}}}}}}}}}}}}}}}}}}}}}}", 158 | "}}}}}}}} +$$i{IU{igt,---------------------------------------------------------------------;2dhzzzhdu;---:}}}}}}}}}}}}}}}}}}}}}}}", 159 | "}}}}}}}} +$$i{nR/git:------------------------------------------------------------------1lY]{{{___`{{{Tl2}}}}}}}}}}}}}}}}}}}}}}}}", 160 | "}}}}}}}} @$$i{cQTgtt=-------------------------*------------------------------------*;dY{`KA088888840ZJ`{/}}}}}}}}}}}}}}}}}}}}}}}", 161 | "}}}}}}}} +$$i{z/Bgtt---------------------------------------------------------------dW{Lr888888888888884eK{{}}}}}}}}}}}}}}}}}}}}}", 162 | "}}}}}}}} +$$i{g{cft1-------------------------------------------------------------1V{L988888888888888888889K{{}}}}}}}}}}}}}}}}}}}", 163 | "}}}}}}}} +$$i{s{hst>----------------------------------------=-------------------a/_e888888888eK~ED388888888e_{}}}}}}}}}}}}}}}}}}", 164 | "}}}}}}}} +$$i{g{pst:-----------------------------------------------------------h[K6888888888C{{{{{F8888888884K{}}}}}}}}}}}}}}}}}", 165 | "}}}}}}}} +$$i{g[stt:----------------------------------------------------------l{F88888888884`{{{{{]48888888883D{_}}}}}}}}}}}}}}}", 166 | "}}}}}}}} +$$i[gRptt=---------------------------------------------------------k{S88888888888e{{{{{{{q88888888888D{}}}}}}}}}}}}}}}", 167 | "}}}}}}}} +$$iQzIitt=--------------------------------------------------------a[F888888888888C{{{{{{{r888888888888D{}}}}}}}}}}}}}}", 168 | "}}}}}}}} +$$iRcbit<--------------------------------------------------------,)K8888888888888C{{{{{{{A8888888888888J{}}}}}}}}}}}}}", 169 | "}}}}}}}} +$$iTnkit>--------------------------------------------------------Y_88888888888888r{{{{{{{A88888888888884~]}}}}}}}}}}}}", 170 | "}}}}}}}} +$$iIBsut:-------------------------------------------------------d{w88888888888888e{{{{{{{A88888888888888q{}}}}}}}}}}}}", 171 | "}}}}}}}} @$$imIutt--------------------------------------------------------/K888888888888888q{{{{{{{r888888888888888J{}}}}}}}}}}}", 172 | "}}}}}}}} +$$inBttt-------------------------------------------------------h{78888888888888889{{{{{{{w8888888888888886{}}}}}}}}}}}", 173 | "}}}}}}}} +$$icntt<-------------------------------------------------------WK88888888888888886{{{{{{{78888888888888888J{}}}}}}}}}}", 174 | "}}}}}}}} +$$ikzut,------------------------------------------------------u{e88888888888888886{{{{{{{688888888888888880{}}}}}}}}}}", 175 | "}}}}}}}} @$$igktt>-----------------------------------------------------*v_888888888888888888{{{{{{{388888888888888888~]}}}}}}}}}", 176 | "}}}}}}}} +$%ifftt-------------------------------------------*----------*^H888888888888888888`{{{{{_888888888888888888F{}}}}}}}}}", 177 | "}}}}}}}} #$$igitt------------------------------------------------------>{r388888888888888888~{{{{{~888888888888888888e{}}}}}}}}}", 178 | "}}}}}}}} +$$iitt1------------------------------------------------------p{6888888888888888888E{{{{{P8888888888888888886{}}}}}}}}}", 179 | "}}}}}}}} +$$ittt<------------------------------------------------------h{8888888888888888888L{{{{{J8888888888888888888_}}}}}}}}}", 180 | "}}}}}}}} +$$iitt>------------------------------------------------------v~8888888888888888888J{{{{{F8888888888888888888!]}}}}}}}}", 181 | "}}}}}}}} +$$tttt-------------------------------------------------------mE8888888888888888888G{{{{{D8888888888888888888L`}}}}}}}}", 182 | "}}}}}}}} +$$ittt------------------------------------------------------*NP8888888888888888888D{{{{{C8888888888888888888K`}}}}}}}}", 183 | "}}}}}}}} #$$ittt----------------------------------------------------**-MP8888888888888888888A{{{{{e8888888888888888888K]}}}}}}}}", 184 | "}}}}}}}} +$$ttt<-----------------------------------------------------;*b~8888888888888888888C{{{{{08888888888888888888E]}}}}}}}}", 185 | "}}}}}}}} +$$ttt,**----**-------*-------*--------**----**--**--------***l`8888888888888888888e{{{{{78888888888888888888_}}}}}}}}}", 186 | "}}}}}}}} +$$itt:*=----**---*---*---*---*---*----*=----**--*=---*----**-p{58888888888888888889{{{{{38888888888888888884{}}}}}}}}}", 187 | "}}}}}}}} +$$ttt#++@o@o@@@+o@++o@@+o@++o@o@O@OOO@++@o@o@@@o++++o@OO@OO@@*{e8888888888888888884]{{{E88888888888888888880{}}}}}}}}}", 188 | "}}}}}}}} +$$tt; ^D8888888888888888888J{{{D8888888888888888888S{}}}}}}}}}", 189 | "}}}}}}}}} +$:= vE88888888888888888886E`J48888888888888888888P]}}}}}}}}}", 190 | "}}}}}}}}}} op{7888888888888888888888888888888888888888887{}}}}}}}}}}", 191 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}{F88888888888888888888888888888888888888888D{}}}}}}}}}}", 192 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}`]58888888888888888888888888888888888888884_]}}}}}}}}}}", 193 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}{D888888888888888888838888888888888888888S{}}}}}}}}}}}", 194 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}]]788888888888888887DJD788888888888888886]]}}}}}}}}}}}", 195 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}{P888888888888888e]{{{]e888888888888883K{}}}}}}}}}}}}", 196 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}{D88888888888885_{{{{{]58888888888888A{}}}}}}}}}}}}}", 197 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}]{e888888888888r{{{{{{{r888888888883e{`}}}}}}}}}}}}}", 198 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}{]w88888888888r{{{{{{{A888888888880]]}}}}}}}}}}}}}}", 199 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}]]e88888888880{{{{{{{w8888888888w`]}}}}}}}}}}}}}}}", 200 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}]{A3888888883L{{{{{E3888888888Z{]}}}}}}}}}}}}}}}}", 201 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}]{K6888888884J]{{J4888888886K{]}}}}}}}}}}}}}}}}}", 202 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}{]A488888888464888888883A`{}}}}}}}}}}}}}}}}}}}", 203 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}]{_D58888888888888886A_{]}}}}}}}}}}}}}}}}}}}}", 204 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}]{{KZ73888888887CK]{]}}}}}}}}}}}}}}}}}}}}}}", 205 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}{{{]!JJFJJE]{{{}}}}}}}}}}}}}}}}}}}}}}}}}", 206 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}]]]]]]]}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 207 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 208 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 209 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 210 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 211 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 212 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 213 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 214 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 215 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 216 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 217 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 218 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 219 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 220 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 221 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 222 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 223 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}", 224 | "}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}" 225 | }; 226 | -------------------------------------------------------------------------------- /encfsgui_volume.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | frmVolume 4 | 5 | 6 | 7 | 0 8 | 0 9 | 620 10 | 780 11 | 12 | 13 | 14 | 15 | 0 16 | 0 17 | 18 | 19 | 20 | 21 | 620 22 | 780 23 | 24 | 25 | 26 | 27 | 620 28 | 780 29 | 30 | 31 | 32 | Qt::StrongFocus 33 | 34 | 35 | Create a new encfs encrypted volume 36 | 37 | 38 | 39 | 40 | 10 41 | 0 42 | 601 43 | 191 44 | 45 | 46 | 47 | Volume settings: 48 | 49 | 50 | false 51 | 52 | 53 | 54 | 55 | 10 56 | 30 57 | 191 58 | 16 59 | 60 | 61 | 62 | Unique volume name: 63 | 64 | 65 | 66 | 67 | 68 | 10 69 | 50 70 | 581 71 | 21 72 | 73 | 74 | 75 | 76 | 77 | 78 | 10 79 | 80 80 | 481 81 | 16 82 | 83 | 84 | 85 | Location of new (empty) encfs folder: 86 | 87 | 88 | 89 | 90 | 91 | 10 92 | 100 93 | 481 94 | 21 95 | 96 | 97 | 98 | 99 | 100 | 101 | 490 102 | 90 103 | 101 104 | 41 105 | 106 | 107 | 108 | Browse 109 | 110 | 111 | 112 | 113 | 114 | 10 115 | 130 116 | 331 117 | 16 118 | 119 | 120 | 121 | Destination (mount) folder: 122 | 123 | 124 | 125 | 126 | 127 | 10 128 | 150 129 | 481 130 | 21 131 | 132 | 133 | 134 | 135 | 136 | 137 | 490 138 | 140 139 | 101 140 | 41 141 | 142 | 143 | 144 | Browse 145 | 146 | 147 | 148 | 149 | 150 | 151 | 10 152 | 460 153 | 601 154 | 121 155 | 156 | 157 | 158 | Password options: 159 | 160 | 161 | 162 | 163 | 160 164 | 30 165 | 241 166 | 21 167 | 168 | 169 | 170 | QLineEdit::Password 171 | 172 | 173 | 174 | 175 | 176 | 20 177 | 30 178 | 91 179 | 16 180 | 181 | 182 | 183 | Password: 184 | 185 | 186 | 187 | 188 | 189 | 20 190 | 60 191 | 121 192 | 16 193 | 194 | 195 | 196 | Repeat password: 197 | 198 | 199 | 200 | 201 | 202 | 160 203 | 60 204 | 241 205 | 21 206 | 207 | 208 | 209 | QLineEdit::Password 210 | 211 | 212 | 213 | 214 | 215 | 20 216 | 90 217 | 211 218 | 20 219 | 220 | 221 | 222 | Save password in keychain 223 | 224 | 225 | 226 | 227 | 228 | 229 | 10 230 | 580 231 | 601 232 | 161 233 | 234 | 235 | 236 | mount options: 237 | 238 | 239 | 240 | 241 | 10 242 | 30 243 | 211 244 | 16 245 | 246 | 247 | 248 | encfs/gocryptfs mount options: 249 | 250 | 251 | 252 | 253 | 254 | 230 255 | 30 256 | 331 257 | 21 258 | 259 | 260 | 261 | 262 | 263 | 264 | 10 265 | 60 266 | 481 267 | 20 268 | 269 | 270 | 271 | Automatically mount this volume when application starts 272 | 273 | 274 | 275 | 276 | 277 | 10 278 | 80 279 | 431 280 | 20 281 | 282 | 283 | 284 | Prevent auto-unmounting this volume on application exit 285 | 286 | 287 | 288 | 289 | 290 | 10 291 | 100 292 | 571 293 | 20 294 | 295 | 296 | 297 | Allow access to 'other' user (you must be root/admin) - needed for Spotlight to work 298 | 299 | 300 | 301 | 302 | 303 | 10 304 | 120 305 | 321 306 | 20 307 | 308 | 309 | 310 | Mount as a local volume 311 | 312 | 313 | 314 | 315 | 316 | 10 317 | 140 318 | 581 319 | 20 320 | 321 | 322 | 323 | Use 'sudo' to mount this volume. (sudo must be configured not to require a password!) 324 | 325 | 326 | 327 | 328 | 329 | 330 | 380 331 | 750 332 | 113 333 | 32 334 | 335 | 336 | 337 | Create 338 | 339 | 340 | 341 | 342 | 343 | 500 344 | 750 345 | 113 346 | 32 347 | 348 | 349 | 350 | Cancel 351 | 352 | 353 | 354 | 355 | 356 | 10 357 | 240 358 | 601 359 | 221 360 | 361 | 362 | 363 | QTabWidget::Triangular 364 | 365 | 366 | 1 367 | 368 | 369 | Qt::ElideMiddle 370 | 371 | 372 | 373 | gocryptfs options 374 | 375 | 376 | 377 | 378 | 0 379 | 0 380 | 591 381 | 191 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 20 391 | 10 392 | 211 393 | 20 394 | 395 | 396 | 397 | Do not encrypt filenames 398 | 399 | 400 | 401 | 402 | 403 | 20 404 | 40 405 | 211 406 | 20 407 | 408 | 409 | 410 | Use AES-SIV encryption 411 | 412 | 413 | 414 | 415 | 416 | 417 | encfs options 418 | 419 | 420 | 421 | 422 | 0 423 | 0 424 | 591 425 | 191 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 30 435 | 120 436 | 141 437 | 16 438 | 439 | 440 | 441 | Filename encoding: 442 | 443 | 444 | 445 | 446 | 447 | 440 448 | 90 449 | 104 450 | 26 451 | 452 | 453 | 454 | 455 | 456 | 457 | 450 458 | 70 459 | 60 460 | 16 461 | 462 | 463 | 464 | Blocksize: 465 | 466 | 467 | 468 | 469 | 470 | 10 471 | 0 472 | 571 473 | 61 474 | 475 | 476 | 477 | encfs profile 478 | 479 | 480 | 481 | 482 | 10 483 | 30 484 | 100 485 | 20 486 | 487 | 488 | 489 | Balanced 490 | 491 | 492 | true 493 | 494 | 495 | 496 | 497 | 498 | 140 499 | 30 500 | 100 501 | 20 502 | 503 | 504 | 505 | Performance 506 | 507 | 508 | 509 | 510 | 511 | 280 512 | 30 513 | 100 514 | 20 515 | 516 | 517 | 518 | Secure 519 | 520 | 521 | 522 | 523 | 524 | 440 525 | 30 526 | 100 527 | 20 528 | 529 | 530 | 531 | Custom 532 | 533 | 534 | 535 | 536 | 537 | 538 | 460 539 | 170 540 | 87 541 | 20 542 | 543 | 544 | 545 | External IV 546 | 547 | 548 | 549 | 550 | 551 | 340 552 | 170 553 | 111 554 | 20 555 | 556 | 557 | 558 | Chained IV 559 | 560 | 561 | 562 | 563 | 564 | 30 565 | 140 566 | 181 567 | 26 568 | 569 | 570 | 571 | 572 | 573 | 574 | 270 575 | 70 576 | 60 577 | 16 578 | 579 | 580 | 581 | Keysize: 582 | 583 | 584 | 585 | 586 | 587 | 30 588 | 170 589 | 131 590 | 20 591 | 592 | 593 | 594 | Per-block HMAC 595 | 596 | 597 | 598 | 599 | 600 | 30 601 | 90 602 | 181 603 | 26 604 | 605 | 606 | 607 | 608 | 609 | 610 | 30 611 | 70 612 | 121 613 | 16 614 | 615 | 616 | 617 | Cipher algorithm: 618 | 619 | 620 | 621 | 622 | 623 | 180 624 | 170 625 | 141 626 | 20 627 | 628 | 629 | 630 | Per-file unique IV 631 | 632 | 633 | 634 | 635 | 636 | 260 637 | 90 638 | 131 639 | 26 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 10 650 | 190 651 | 601 652 | 45 653 | 654 | 655 | 656 | Volume type: 657 | 658 | 659 | 660 | 661 | 150 662 | 20 663 | 100 664 | 20 665 | 666 | 667 | 668 | encfs 669 | 670 | 671 | 672 | 673 | 674 | 10 675 | 20 676 | 100 677 | 20 678 | 679 | 680 | 681 | gocryptfs 682 | 683 | 684 | true 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | -------------------------------------------------------------------------------- /encfsgui_main.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | frmMainDialog 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 1120 13 | 462 14 | 15 | 16 | 17 | 18 | 0 19 | 0 20 | 21 | 22 | 23 | 24 | 1120 25 | 462 26 | 27 | 28 | 29 | 30 | 1120 31 | 462 32 | 33 | 34 | 35 | 36 | 1120 37 | 462 38 | 39 | 40 | 41 | 42 | Jura 43 | 12 44 | 45 | 46 | 47 | encFSGui 3.01 48 | 49 | 50 | 51 | bitmaps/encfsgui.icnsbitmaps/encfsgui.icns 52 | 53 | 54 | true 55 | 56 | 57 | false 58 | 59 | 60 | false 61 | 62 | 63 | 64 | 65 | -1 66 | -1 67 | 981 68 | 87 69 | 70 | 71 | 72 | 73 | 0 74 | 0 75 | 76 | 77 | 78 | QFrame::NoFrame 79 | 80 | 81 | QFrame::Raised 82 | 83 | 84 | 85 | QLayout::SetFixedSize 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 231 96 | 231 97 | 231 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 231 107 | 231 108 | 231 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 231 118 | 231 119 | 231 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | Jura 129 | 11 130 | 131 | 132 | 133 | true 134 | 135 | 136 | Create 137 | 138 | 139 | 140 | bitmaps/createfolder.pngbitmaps/createfolder.png 141 | 142 | 143 | 144 | 32 145 | 32 146 | 147 | 148 | 149 | QToolButton::DelayedPopup 150 | 151 | 152 | Qt::ToolButtonTextUnderIcon 153 | 154 | 155 | true 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | Jura 164 | 11 165 | 166 | 167 | 168 | Add Existing 169 | 170 | 171 | 172 | bitmaps/existingfolder.pngbitmaps/existingfolder.png 173 | 174 | 175 | 176 | 32 177 | 32 178 | 179 | 180 | 181 | Qt::ToolButtonTextUnderIcon 182 | 183 | 184 | true 185 | 186 | 187 | 188 | 189 | 190 | 191 | Qt::Horizontal 192 | 193 | 194 | QSizePolicy::Fixed 195 | 196 | 197 | 198 | 20 199 | 20 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | Jura 209 | 11 210 | 211 | 212 | 213 | Browse 214 | 215 | 216 | 217 | bitmaps/browsefolder.pngbitmaps/browsefolder.png 218 | 219 | 220 | 221 | 32 222 | 32 223 | 224 | 225 | 226 | Qt::ToolButtonTextUnderIcon 227 | 228 | 229 | true 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | Jura 238 | 11 239 | 240 | 241 | 242 | Remove 243 | 244 | 245 | 246 | bitmaps/removefolder.pngbitmaps/removefolder.png 247 | 248 | 249 | 250 | 32 251 | 32 252 | 253 | 254 | 255 | Qt::ToolButtonTextUnderIcon 256 | 257 | 258 | true 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | Jura 267 | 11 268 | 269 | 270 | 271 | Info 272 | 273 | 274 | 275 | bitmaps/folderinfo.pngbitmaps/folderinfo.png 276 | 277 | 278 | 279 | 32 280 | 32 281 | 282 | 283 | 284 | Qt::ToolButtonTextUnderIcon 285 | 286 | 287 | true 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | Jura 296 | 11 297 | 298 | 299 | 300 | Edit 301 | 302 | 303 | 304 | bitmaps/editfolder.pngbitmaps/editfolder.png 305 | 306 | 307 | 308 | 32 309 | 32 310 | 311 | 312 | 313 | Qt::ToolButtonTextUnderIcon 314 | 315 | 316 | true 317 | 318 | 319 | 320 | 321 | 322 | 323 | Qt::Horizontal 324 | 325 | 326 | QSizePolicy::Fixed 327 | 328 | 329 | 330 | 20 331 | 20 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | Jura 341 | 11 342 | 343 | 344 | 345 | Refresh 346 | 347 | 348 | 349 | bitmaps/refresh.pngbitmaps/refresh.png 350 | 351 | 352 | 353 | 32 354 | 32 355 | 356 | 357 | 358 | Qt::ToolButtonTextUnderIcon 359 | 360 | 361 | true 362 | 363 | 364 | 365 | 366 | 367 | 368 | Qt::Horizontal 369 | 370 | 371 | QSizePolicy::Fixed 372 | 373 | 374 | 375 | 20 376 | 20 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | Jura 386 | 11 387 | 50 388 | false 389 | 390 | 391 | 392 | false 393 | 394 | 395 | Mount 396 | 397 | 398 | 399 | bitmaps/icons8-unlock-32.pngbitmaps/icons8-unlock-32.png 400 | 401 | 402 | 403 | 32 404 | 32 405 | 406 | 407 | 408 | Qt::ToolButtonTextUnderIcon 409 | 410 | 411 | true 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | Jura 420 | 11 421 | 422 | 423 | 424 | Unmount 425 | 426 | 427 | 428 | bitmaps/icons8-lock-32.pngbitmaps/icons8-lock-32.png 429 | 430 | 431 | 432 | 32 433 | 32 434 | 435 | 436 | 437 | Qt::ToolButtonTextUnderIcon 438 | 439 | 440 | true 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | Jura 449 | 11 450 | 451 | 452 | 453 | Force unmount all 454 | 455 | 456 | 457 | bitmaps/icons8-protect-32.pngbitmaps/icons8-protect-32.png 458 | 459 | 460 | 461 | 32 462 | 32 463 | 464 | 465 | 466 | Qt::ToolButtonTextUnderIcon 467 | 468 | 469 | true 470 | 471 | 472 | 473 | 474 | 475 | 476 | Qt::Horizontal 477 | 478 | 479 | QSizePolicy::Fixed 480 | 481 | 482 | 483 | 15 484 | 20 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | Jura 494 | 11 495 | 496 | 497 | 498 | Settings 499 | 500 | 501 | 502 | bitmaps/settings.pngbitmaps/settings.png 503 | 504 | 505 | 506 | 32 507 | 32 508 | 509 | 510 | 511 | Qt::ToolButtonTextUnderIcon 512 | 513 | 514 | true 515 | 516 | 517 | 518 | 519 | 520 | 521 | Qt::Horizontal 522 | 523 | 524 | QSizePolicy::Fixed 525 | 526 | 527 | 528 | 15 529 | 20 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | Jura 539 | 11 540 | 541 | 542 | 543 | Hide 544 | 545 | 546 | 547 | bitmaps/hide.pngbitmaps/hide.png 548 | 549 | 550 | 551 | 32 552 | 32 553 | 554 | 555 | 556 | Qt::ToolButtonTextUnderIcon 557 | 558 | 559 | true 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | Jura 568 | 11 569 | 570 | 571 | 572 | Quit 573 | 574 | 575 | 576 | bitmaps/quit.pngbitmaps/quit.png 577 | 578 | 579 | 580 | 32 581 | 32 582 | 583 | 584 | 585 | Qt::ToolButtonTextUnderIcon 586 | 587 | 588 | true 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 10 598 | 90 599 | 1100 600 | 341 601 | 602 | 603 | 604 | 605 | 0 606 | 0 607 | 608 | 609 | 610 | QFrame::StyledPanel 611 | 612 | 613 | QFrame::Raised 614 | 615 | 616 | 617 | 618 | 0 619 | 0 620 | 1101 621 | 341 622 | 623 | 624 | 625 | 626 | 0 627 | 0 628 | 629 | 630 | 631 | 632 | Jura 633 | 11 634 | 635 | 636 | 637 | Qt::ClickFocus 638 | 639 | 640 | QAbstractItemView::NoEditTriggers 641 | 642 | 643 | false 644 | 645 | 646 | true 647 | 648 | 649 | QAbstractItemView::SingleSelection 650 | 651 | 652 | QAbstractItemView::SelectRows 653 | 654 | 655 | Qt::DashLine 656 | 657 | 658 | true 659 | 660 | 661 | false 662 | 663 | 664 | false 665 | 666 | 667 | false 668 | 669 | 670 | 671 | 672 | 673 | 674 | 10 675 | 440 676 | 741 677 | 16 678 | 679 | 680 | 681 | 682 | Jura 683 | 12 684 | 685 | 686 | 687 | QFrame::Panel 688 | 689 | 690 | QFrame::Sunken 691 | 692 | 693 | lbl_InfoLabel 694 | 695 | 696 | 697 | 698 | 699 | 759 700 | 440 701 | 351 702 | 16 703 | 704 | 705 | 706 | 707 | Jura 708 | 11 709 | 710 | 711 | 712 | QFrame::Panel 713 | 714 | 715 | QFrame::Sunken 716 | 717 | 718 | lbl_UpdateState 719 | 720 | 721 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 722 | 723 | 724 | 725 | 726 | 727 | 728 | -------------------------------------------------------------------------------- /encfsgui_helper.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import sys 4 | import time 5 | import datetime 6 | import string 7 | import subprocess 8 | import inspect 9 | import traceback 10 | 11 | from PyQt5 import uic 12 | from PyQt5 import QtWidgets 13 | from PyQt5.QtWidgets import * 14 | from PyQt5 import QtCore 15 | 16 | 17 | try: 18 | import base64 19 | import hashlib 20 | from Crypto import Random 21 | from Crypto.Cipher import AES 22 | except: 23 | oops = QApplication([]) 24 | QtWidgets.QMessageBox.critical(None,"Error loading pycrypto library","This version of pyencfsgui requires the 'pycrypto' library.\n\nPlease install using\n'python3 -m pip install pycrypto --user'\n") 25 | sys.exit(1) 26 | 27 | import encfsgui_globals 28 | from encfsgui_globals import * 29 | 30 | import cgetmasterkey 31 | from cgetmasterkey import CMasterKeyWindow 32 | 33 | 34 | ################################# 35 | ### METHODS, HELPER FUNCTIONS ### 36 | ################################# 37 | 38 | def getCurDir(): 39 | print_debug("Start %s" % inspect.stack()[0][3]) 40 | curdir = "./" 41 | try: 42 | os.chdir(os.path.dirname(os.path.abspath(sys.argv[0]))) 43 | curdir = os.getcwd() 44 | except Exception as e: 45 | print_debug("Unable to determine current directory: %s" % str(e)) 46 | pass 47 | return curdir 48 | 49 | def getNow(): 50 | return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") 51 | 52 | def getVersion(): 53 | thisversion = "" 54 | versionlines = readfile("version.txt") 55 | for thisline in versionlines: 56 | if not thisline.strip() == "": 57 | thisversion = thisline.strip() 58 | break 59 | return thisversion 60 | 61 | def readfile(filename): 62 | print_debug("Start %s" % inspect.stack()[0][3]) 63 | print_debug("Reading %s" % filename) 64 | contents = [] 65 | try: 66 | f = open(filename,"r") 67 | contents = f.readlines() 68 | f.close() 69 | print_debug("Read %d lines" % len(contents)) 70 | except Exception as e: 71 | print_debug("Unable to read file %s: %s" % (filename, str(e))) 72 | pass 73 | return contents 74 | 75 | def writefile(filename,contents): 76 | f = open(filename,'a') 77 | for l in contents: 78 | f.write("%s\n" % l) 79 | f.close() 80 | return 81 | 82 | def createFile(filename): 83 | f = open(filename,'w+') 84 | f.write("# file created at %s\n" % getNow()) 85 | f.close() 86 | return 87 | 88 | def print_debug(line): 89 | if encfsgui_globals.debugmode: 90 | #try: 91 | # print("%s - DEBUG : %s" % (getNow(), str(line))) 92 | #except Exception: 93 | # print(traceback.format_exc()) 94 | writefile(encfsgui_globals.logfile, ["%s : %s" % (getNow(), str(line)) ] ) 95 | return 96 | 97 | def getTmpName(): 98 | return datetime.datetime.now().strftime("%Y%m%d%H%M%S") 99 | 100 | def getEncFSVersion(): 101 | print_debug("Start %s" % inspect.stack()[0][3]) 102 | encfsversion = "" 103 | oscmd = encfsgui_globals.g_Settings["encfspath"] 104 | cmdargs = '--version' 105 | cmdoutput = execOSCmd("%s %s" % (oscmd,cmdargs )) 106 | outputline = cmdoutput[0] 107 | for line in cmdoutput: 108 | if line.startswith("encfs "): 109 | outputline = line 110 | break 111 | outputparts = outputline.split(" ") 112 | encfsversion = outputparts[-1].replace('\n','') 113 | return encfsversion 114 | 115 | 116 | def getGoCryptFSVersion(): 117 | print_debug("Start %s" % inspect.stack()[0][3]) 118 | gocryptfsversion = "" 119 | oscmd = encfsgui_globals.g_Settings["gocryptfspath"] 120 | cmdargs = '--version' 121 | cmdoutput = execOSCmd("%s %s" % (oscmd,cmdargs )) 122 | outputline = cmdoutput[0] 123 | for line in cmdoutput: 124 | if line.startswith("gocryptfs "): 125 | outputline = line 126 | break 127 | outputparts = outputline.split(";") 128 | gocryptfspart = outputparts[0] 129 | versionparts = gocryptfspart.split(" ") 130 | gocryptfsversion = versionparts[-1].replace('\n','') 131 | return gocryptfsversion 132 | 133 | 134 | def ifExists(encfsapp="encfs"): 135 | print_debug("Start %s" % inspect.stack()[0][3]) 136 | encbinfound = False 137 | dictkey = "%spath" % encfsapp 138 | if dictkey in encfsgui_globals.g_Settings: 139 | encbinpath = encfsgui_globals.g_Settings[dictkey] 140 | if os.path.exists(encbinpath): 141 | encbinfound = True 142 | return encbinfound 143 | 144 | 145 | def getOSType(): 146 | return platform.system() 147 | 148 | 149 | def ismacOS(): 150 | if "darwin" in getOSType().lower(): 151 | return True 152 | else: 153 | return False 154 | 155 | def isLinux(): 156 | if "linux" in getOSType().lower(): 157 | return True 158 | else: 159 | return False 160 | 161 | def isWindows(): 162 | if "windows" in getOSType().lower(): 163 | return True 164 | else: 165 | return False 166 | 167 | def runwhich(binarytofind): 168 | whichlocation = "" 169 | if binarytofind != "": 170 | cmd = ["which",binarytofind] 171 | cmdlines, cmdretval = execOSCmdRetVal(cmd) 172 | # get the first line that contains the binarytofind 173 | for line in cmdlines: 174 | if binarytofind in line: 175 | if os.path.exists(line.strip()): 176 | whichlocation = line.strip() 177 | break 178 | return whichlocation 179 | 180 | 181 | def execOSCmd(cmd): 182 | print_debug("Start %s" % inspect.stack()[0][3]) 183 | if not cmd.startswith("sh -c"): 184 | print_debug("Executing [ %s ]" % cmd) 185 | else: 186 | print_debug("Note: A part of the command below will not shown because it might contain sensitive information (password)") 187 | cmdparts = cmd.split("|") 188 | if len(cmdparts) > 1: 189 | print_debug("Executing [ %s ]" % cmdparts[1]) 190 | p = subprocess.Popen('%s' % cmd, 191 | stdout=subprocess.PIPE, 192 | stderr=subprocess.STDOUT, 193 | shell=True) 194 | outputobj = iter(p.stdout.readline, b'') 195 | outputlines = [] 196 | for l in outputobj: 197 | thisline = l.decode() 198 | outputlines.append(thisline.replace('\\n','').replace("'","")) 199 | return outputlines 200 | 201 | 202 | def execOSCmdRetVal(cmdarray): 203 | print_debug("Start %s" % inspect.stack()[0][3]) 204 | print_debug("Executing %s" % cmdarray) 205 | returncode = 0 206 | outputlines = [] 207 | p = subprocess.run(cmdarray, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) 208 | returncode = p.returncode 209 | outputobj = p.stdout.split('\n') 210 | for thisline in outputobj: 211 | outputlines.append(thisline.replace('\\n','').replace("'","")) 212 | print_debug("Return code: %s" % str(returncode)) 213 | return outputlines, returncode 214 | 215 | 216 | def execOScmdAsync(cmdarray): 217 | print_debug("Start %s" % inspect.stack()[0][3]) 218 | print_debug("Executing %s" % cmdarray) 219 | p = subprocess.Popen(cmdarray) 220 | #p.terminate() 221 | return 222 | 223 | def openFolder(foldername): 224 | print_debug("Start %s" % inspect.stack()[0][3]) 225 | if ismacOS(): 226 | subprocess.call(["open", "-R", foldername + "/"]) 227 | elif isLinux(): 228 | subprocess.call(["xdg-open", foldername + "/"]) 229 | return 230 | 231 | def getKeyChainPassword(volumename): 232 | print_debug("Start %s" % inspect.stack()[0][3]) 233 | objname = "EncFSGUI_%s" % volumename 234 | cmd = "sh -c \"security find-generic-password -a '%s' -s '%s' -w login.keychain\"" % (objname, objname) 235 | passarr = execOSCmd(cmd) 236 | password = str(passarr[0]).replace("\n","").strip() 237 | return password 238 | 239 | def autoUpdate(): 240 | #return values: 241 | # -1 = error 242 | # 0 = ok, no update 243 | # 1 = update found 244 | 245 | updateresult = 0 # up to date 246 | 247 | print_debug("Start %s" % inspect.stack()[0][3]) 248 | cmdarr = ["git","pull"] 249 | gitoutput, gitretval = execOSCmdRetVal(cmdarr) 250 | 251 | if (encfsgui_globals.debugmode): 252 | print_debug("Git output:") 253 | 254 | if gitretval != 0: 255 | updateresult = -1 256 | else: 257 | 258 | updatefound = False 259 | for l in gitoutput: 260 | if (encfsgui_globals.debugmode): 261 | print_debug(" >> %s" % l) 262 | if ":" in l and not "fatal" in l and not "not staged" in l and not "hint:" in l: 263 | updatefound = True 264 | break 265 | 266 | if updatefound: 267 | updateresult = 1 # update found 268 | 269 | return updateresult, gitoutput 270 | 271 | def getExpectScriptContents(scripttype, insertbreak = False): 272 | print_debug("Start %s" % inspect.stack()[0][3]) 273 | scriptcontents = "" 274 | if scripttype == "encfs": 275 | # header 276 | newline = "#!/usr/bin/env expect\n" 277 | scriptcontents += newline 278 | 279 | newline = "set passwd [lindex $argv 0]\n" 280 | scriptcontents += newline 281 | 282 | newline = "set timeout 10\n" 283 | scriptcontents += newline 284 | 285 | # launch encfs 286 | newline = "spawn \"$ENCFSBIN\" -v \"$ENCPATH\" \"$MOUNTPATH\"\n" 287 | scriptcontents += newline 288 | 289 | # activate expert mode 290 | newline = "expect \"Please choose from one of the following options:\"\n" 291 | scriptcontents += newline 292 | newline = "expect \"?>\"\n" 293 | scriptcontents += newline 294 | newline = "send \"x\\n\"\n" 295 | scriptcontents += newline 296 | 297 | # set cipher algorithm 298 | newline = "expect \"Enter the number corresponding to your choice: \"\n" 299 | scriptcontents += newline 300 | newline = "send \"$CIPHERALGO\\n\"\n" 301 | scriptcontents += newline 302 | 303 | # select cipher keysize 304 | newline = "expect \"Selected key size:\"\n" 305 | scriptcontents += newline 306 | newline = "send \"$CIPHERKEYSIZE\\n\"\n" 307 | scriptcontents += newline 308 | 309 | # select filesystem block size 310 | newline = "expect \"filesystem block size:\"\n" 311 | scriptcontents += newline 312 | newline = "send \"$BLOCKSIZE\\n\"\n" 313 | scriptcontents += newline 314 | 315 | # select encoding algo 316 | newline = "expect \"Enter the number corresponding to your choice: \"\n" 317 | scriptcontents += newline 318 | newline = "send \"$ENCODINGALGO\\n\"\n" 319 | scriptcontents += newline 320 | 321 | if (insertbreak): 322 | newline = "break\n" 323 | scriptcontents += newline 324 | 325 | # filename IV chaining 326 | newline = "expect \"Enable filename initialization vector chaining?\"\n" 327 | scriptcontents += newline 328 | newline = "send \"$IVCHAINING\\n\"\n" 329 | scriptcontents += newline 330 | 331 | # per filename IV 332 | newline = "expect \"Enable per-file initialization vectors?\"\n" 333 | scriptcontents += newline 334 | newline = "send \"$PERFILEIV\\n\"\n" 335 | scriptcontents += newline 336 | 337 | # file to IV header chaining can only be used when both previous options are enabled 338 | # which means it might slide to the next option right away 339 | newline = "expect {\n" 340 | scriptcontents += newline 341 | newline = "\t\"Enable filename to IV header chaining?\" {\n" 342 | scriptcontents += newline 343 | newline = "\t\tsend \"$FILETOIVHEADERCHAINING\\n\"\n" 344 | scriptcontents += newline 345 | newline = "\t\texpect \"Enable block authentication code headers\"\n" 346 | scriptcontents += newline 347 | newline = "\t\tsend \"$BLOCKAUTHCODEHEADERS\\n\"\n" 348 | scriptcontents += newline 349 | newline = "\t\t}\n" 350 | scriptcontents += newline 351 | newline = "\t\"Enable block authentication code headers\" {\n" #space matters 352 | scriptcontents += newline 353 | newline = "\t\tsend \"$BLOCKAUTHCODEHEADERS\\n\"\n" 354 | scriptcontents += newline 355 | newline = "\t\t}\n" 356 | scriptcontents += newline 357 | newline = "\t}\n" 358 | scriptcontents += newline 359 | 360 | # add random bytes to each block header 361 | newline = "expect \"Select a number of bytes, from 0 (no random bytes) to 8: \"\n" 362 | scriptcontents += newline 363 | newline = "send \"0\\n\"\n" 364 | scriptcontents += newline 365 | 366 | # file-hole pass-through 367 | newline = "expect \"Enable file-hole pass-through?\"\n" 368 | scriptcontents += newline 369 | newline = "send \"\\n\"\n" 370 | scriptcontents += newline 371 | 372 | # password 373 | newline = "expect \"New Encfs Password: \"\n" 374 | scriptcontents += newline 375 | newline = "send \"$passwd\\n\"\n" 376 | scriptcontents += newline 377 | 378 | newline = "expect \"Verify Encfs Password: \"\n" 379 | scriptcontents += newline 380 | newline = "send \"$passwd\\n\"\n" 381 | scriptcontents += newline 382 | 383 | newline = "puts \"\\nDone.\\n\"\n" 384 | scriptcontents += newline 385 | 386 | newline = "expect \"\\n\"\n" 387 | scriptcontents += newline 388 | 389 | newline = "sleep x\n" 390 | scriptcontents += newline 391 | 392 | if scripttype == "gocryptfs": 393 | # header 394 | newline = "#!/usr/bin/env expect\n" 395 | scriptcontents += newline 396 | 397 | newline = "set passwd [lindex $argv 0]\n" 398 | scriptcontents += newline 399 | 400 | newline = "set timeout 10\n" 401 | scriptcontents += newline 402 | 403 | # launch encfs 404 | newline = "spawn \"$GOCRYPTFSBIN\" $EXTRAOPTS -init \"$ENCPATH\" \n" 405 | scriptcontents += newline 406 | 407 | # password 408 | newline = "expect \"Password:\"\n" 409 | scriptcontents += newline 410 | newline = "send \"$passwd\\n\"\n" 411 | scriptcontents += newline 412 | 413 | newline = "expect \"Repeat:\"\n" 414 | scriptcontents += newline 415 | newline = "send \"$passwd\\n\"\n" 416 | scriptcontents += newline 417 | 418 | newline = "expect \"\\n\"\n" 419 | scriptcontents += newline 420 | 421 | newline = "sleep x\n" 422 | scriptcontents += newline 423 | 424 | 425 | return scriptcontents 426 | 427 | def determineFileNameEncodings(): 428 | print_debug("Start %s" % inspect.stack()[0][3]) 429 | encodings = [] 430 | # create 2 new tmp folders 431 | tmpname = getTmpName() 432 | cwd = os.getcwd() 433 | tmpfolder_enc = os.path.join(cwd, tmpname+"_enc") 434 | tmpfolder_mnt = os.path.join(cwd, tmpname+"_mnt") 435 | if os.path.exists(tmpfolder_enc): 436 | os.removedirs(tmpfolder_enc) 437 | if os.path.exists(tmpfolder_mnt): 438 | os.removedirs(tmpfolder_mnt) 439 | 440 | os.mkdir(tmpfolder_enc) 441 | os.mkdir(tmpfolder_mnt) 442 | 443 | if os.path.exists(encfsgui_globals.g_Settings["encfspath"]): 444 | 445 | # try to create an encrypted volume, and parse output to determine the available fileencoding options 446 | # argument True is needed to insert 'break' after getting the encoding options 447 | scriptcontents = getExpectScriptContents("encfs",True) 448 | password = "boguspassword" 449 | 450 | # replace variables in the script 451 | scriptcontents = scriptcontents.replace("$ENCFSBIN", encfsgui_globals.g_Settings["encfspath"]) 452 | scriptcontents = scriptcontents.replace("$ENCPATH", tmpfolder_enc) 453 | scriptcontents = scriptcontents.replace("$MOUNTPATH", tmpfolder_mnt) 454 | if not str(getEncFSVersion()).startswith("1.9."): 455 | scriptcontents = scriptcontents.replace("$CIPHERALGO", "1") 456 | else: 457 | scriptcontents = scriptcontents.replace("$CIPHERALGO", "1") 458 | scriptcontents = scriptcontents.replace("$CIPHERKEYSIZE", "128") 459 | scriptcontents = scriptcontents.replace("$BLOCKSIZE", "1024") 460 | scriptcontents = scriptcontents.replace("$ENCODINGALGO", "1") 461 | scriptcontents = scriptcontents.replace("$IVCHAINING","") 462 | scriptcontents = scriptcontents.replace("$PERFILEIV","") 463 | scriptcontents = scriptcontents.replace("$FILETOIVHEADERCHAINING","y") 464 | scriptcontents = scriptcontents.replace("$BLOCKAUTHCODEHEADERS","") 465 | scriptcontents = scriptcontents.replace("sleep x","expect eof") 466 | 467 | expectfilename = "expect.encfsgui" 468 | # write script to file 469 | scriptfile = open(expectfilename, 'w') 470 | scriptfile.write(scriptcontents) 471 | scriptfile.close() 472 | # run script file 473 | cmd = "expect '%s' '%s'" % (expectfilename, password) 474 | expectoutput = execOSCmd(cmd) 475 | 476 | # parse output 477 | startfound = False 478 | endfound = False 479 | rawCaps = [] 480 | for l in expectoutput: 481 | #print_debug(">>> %s" % l) 482 | if not startfound: 483 | if "filename encoding algorithms" in l and "available" in l: 484 | startfound = True 485 | else: 486 | if not "." in l: 487 | endfound = True 488 | else: 489 | rawCaps.append(l) 490 | if startfound and endfound: 491 | break 492 | 493 | if not startfound: 494 | rawCaps.clear() 495 | endfound = False 496 | # maybe encfs is in a different language, try in a different way 497 | for l in expectoutput: 498 | if not startfound: 499 | if "1. Block" in l: 500 | startfound = True 501 | rawCaps.append(l) 502 | else: 503 | if not "." in l: 504 | endfound = True 505 | else: 506 | rawCaps.append(l) 507 | if startfound and endfound: 508 | break 509 | 510 | for l in rawCaps: 511 | capparts = l.split(" ") 512 | if len(capparts) > 1: 513 | thisencoding = capparts[1] 514 | encodings.append(thisencoding) 515 | 516 | else: 517 | print_debug("No encfs binary found at %s" % encfsgui_globals.g_Settings["encfspath"]) 518 | 519 | 520 | if len(encodings) > 0: 521 | encfsgui_globals.g_Encodings = encodings 522 | else: 523 | print_debug("Unable to get filename encodings") 524 | for l in expectoutput: 525 | print_debug("%s" % l) 526 | 527 | # clean up again 528 | os.removedirs(tmpfolder_enc) 529 | os.removedirs(tmpfolder_mnt) 530 | os.remove(expectfilename) 531 | 532 | return 533 | 534 | def getMasterKey(): 535 | print_debug("Start %s" % inspect.stack()[0][3]) 536 | curframe = inspect.currentframe() 537 | calframe = inspect.getouterframes(curframe, 2) 538 | print_debug("%s() Called from: %s()" % (inspect.stack()[0][3],calframe[1][3])) 539 | print_debug("Current length of masterkey: %d" % len(encfsgui_globals.masterkey)) 540 | if len(encfsgui_globals.masterkey) != 32: 541 | frmpassword = CMasterKeyWindow() 542 | frmpassword.setWindowTitle("Please enter master key") 543 | frmpassword.show() 544 | frmpassword.setFocus() 545 | frmpassword.activateWindow() 546 | frmpassword.exec_() 547 | encfsgui_globals.masterkey = str(frmpassword.getPassword()) 548 | print_debug("New length of masterkey: %d" % len(encfsgui_globals.masterkey )) 549 | return 550 | 551 | def encrypt(cleartext): 552 | print_debug("Start %s" % inspect.stack()[0][3]) 553 | curframe = inspect.currentframe() 554 | calframe = inspect.getouterframes(curframe, 2) 555 | print_debug("%s() Called from: %s()" % (inspect.stack()[0][3],calframe[1][3])) 556 | ciphertext = "" 557 | encfsgui_globals.masterkey = str(encfsgui_globals.masterkey) 558 | #print_debug("Current length of masterkey: %d" % len(encfsgui_globals.masterkey)) 559 | obj = AES.new(encfsgui_globals.masterkey, AES.MODE_CBC, '!IVNotSoSecret!!') 560 | while (len(cleartext) % 16 != 0): 561 | # add spaces at the end, we can remove them later 562 | cleartext = cleartext + " " 563 | ciphertext=base64.b64encode(obj.encrypt(cleartext)) 564 | return ciphertext.decode() 565 | 566 | def decrypt(ciphertext): 567 | print_debug("Start %s" % inspect.stack()[0][3]) 568 | curframe = inspect.currentframe() 569 | calframe = inspect.getouterframes(curframe, 2) 570 | print_debug("%s() Called from: %s()" % (inspect.stack()[0][3],calframe[1][3])) 571 | encfsgui_globals.masterkey = str(encfsgui_globals.masterkey) 572 | #print_debug("Current length of masterkey: %d" % len(encfsgui_globals.masterkey)) 573 | cleartext = "" 574 | #print_debug("Requested to decrypt '%s'" % ciphertext) 575 | #print_debug("Base64 decoded: %s" % base64.b64decode(ciphertext)) 576 | obj = AES.new(encfsgui_globals.masterkey, AES.MODE_CBC, '!IVNotSoSecret!!') 577 | cleartext = obj.decrypt(base64.b64decode(ciphertext)) 578 | #remove spaces from the end again 579 | cleartext = cleartext.rstrip() 580 | return cleartext 581 | 582 | def makePW32(key): 583 | print_debug("Start %s" % inspect.stack()[0][3]) 584 | pw = key[0:31] 585 | ccode = 1 586 | while len(pw) < 32: 587 | pw += chr(ccode) 588 | ccode += 1 589 | return pw 590 | 591 | def SavePasswordInKeyChain(volumename, password): 592 | print_debug("Start %s" % inspect.stack()[0][3]) 593 | cmd = "sh -c \"security add-generic-password -U -a 'EncFSGUI_%s' -s 'EncFSGUI_%s' -w '%s' login.keychain\"" % (volumename, volumename, str(password)) 594 | print_debug(cmd) 595 | setpwoutput = execOSCmd(cmd) 596 | return 597 | 598 | def RemovePasswordFromKeyChain(volumename): 599 | print_debug("Start %s" % inspect.stack()[0][3]) 600 | cmd = "sh -c \"security delete-generic-password -a 'EncFSGUI_%s' -s 'EncFSGUI_%s' login.keychain\"" % (volumename, volumename) 601 | print_debug(cmd) 602 | setpwoutput = execOSCmd(cmd) 603 | return -------------------------------------------------------------------------------- /cvolume.py: -------------------------------------------------------------------------------- 1 | # encfsgui Create a new volume 2 | # 3 | # 4 | 5 | import os 6 | import sys 7 | import time 8 | import datetime 9 | import string 10 | 11 | from PyQt5 import uic 12 | from PyQt5 import QtWidgets 13 | from PyQt5.QtWidgets import * 14 | from PyQt5 import QtCore 15 | 16 | import encfsgui_globals 17 | from encfsgui_globals import * 18 | 19 | import encfsgui_helper 20 | from encfsgui_helper import * 21 | 22 | class CVolumeWindow(QtWidgets.QDialog): 23 | def __init__(self): 24 | encfsgui_helper.print_debug("Start CVolumeWindow %s" % inspect.stack()[0][3]) 25 | super(CVolumeWindow, self).__init__() 26 | uic.loadUi('encfsgui_volume.ui', self) 27 | 28 | # disable/remove buttons 29 | self.setWindowFlags(self.windowFlags() | QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowStaysOnTopHint) 30 | self.setWindowFlag(QtCore.Qt.WindowMaximizeButtonHint, False) 31 | self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) 32 | 33 | # 0 = create 34 | # 1 = add 35 | # 2 = edit 36 | self.runmode = 0 # create 37 | 38 | self.origvolumename = "" 39 | 40 | self.encfsfolderbutton = self.findChild(QtWidgets.QPushButton, 'btn_selectencfsfolder') 41 | self.encfsfolderbutton.clicked.connect(self.SelectEncfsFolderClicked) 42 | 43 | self.mountfolderbutton = self.findChild(QtWidgets.QPushButton, 'btn_selectmountfolder') 44 | self.mountfolderbutton.clicked.connect(self.SelectMountFolderClicked) 45 | 46 | self.txt_encfsfolder = self.findChild(QtWidgets.QLineEdit, 'txt_encfsfolder') 47 | self.txt_mountfolder = self.findChild(QtWidgets.QLineEdit, 'txt_mountfolder') 48 | self.txt_volumename = self.findChild(QtWidgets.QLineEdit, 'txt_volumename') 49 | self.txt_password = self.findChild(QtWidgets.QLineEdit, 'txt_password') 50 | self.txt_password2 = self.findChild(QtWidgets.QLineEdit, 'txt_password2') 51 | self.txt_encfsmountoptions = self.findChild(QtWidgets.QLineEdit, 'txt_encfsmountoptions') 52 | 53 | self.chk_saveinkeychain = self.findChild(QtWidgets.QCheckBox, 'chk_saveinkeychain') 54 | self.chk_mountaslocal = self.findChild(QtWidgets.QCheckBox, 'chk_mountaslocal') 55 | self.chk_usesudo = self.findChild(QtWidgets.QCheckBox, 'chk_usesudo') 56 | self.chk_automount = self.findChild(QtWidgets.QCheckBox, 'chk_automount') 57 | self.chk_preventautounmount = self.findChild(QtWidgets.QCheckBox, 'chk_preventautounmount') 58 | self.chk_accesstoother = self.findChild(QtWidgets.QCheckBox, 'chk_accesstoother') 59 | 60 | self.savebutton = self.findChild(QtWidgets.QPushButton, 'btn_save') 61 | self.savebutton.clicked.connect(self.SaveButtonClicked) 62 | 63 | self.cancelbutton = self.findChild(QtWidgets.QPushButton, 'btn_cancel') 64 | self.cancelbutton.clicked.connect(self.CancelButtonClicked) 65 | 66 | self.radio_volumetype_gocryptfs = self.findChild(QtWidgets.QRadioButton, 'radio_type_gocryptfs') 67 | self.radio_volumetype_gocryptfs.clicked.connect(self.SelectVolumeTypeGoCryptFS) 68 | 69 | self.radio_volumetype_encfs = self.findChild(QtWidgets.QRadioButton, 'radio_type_encfs') 70 | self.radio_volumetype_encfs.clicked.connect(self.SelectVolumeTypeEncFS) 71 | 72 | self.radio_balanced = self.findChild(QtWidgets.QRadioButton, 'radio_balanced') 73 | self.radio_performance = self.findChild(QtWidgets.QRadioButton, 'radio_performance') 74 | self.radio_secure = self.findChild(QtWidgets.QRadioButton, 'radio_secure') 75 | self.radio_custom = self.findChild(QtWidgets.QRadioButton, 'radio_custom') 76 | 77 | self.radio_balanced.clicked.connect(self.SelectProfileBalanced) 78 | self.radio_performance.clicked.connect(self.SelectProfilePerformance) 79 | self.radio_secure.clicked.connect(self.SelectProfileSecure) 80 | self.radio_custom.clicked.connect(self.SelectProfileCustom) 81 | 82 | self.chk_perblockhmac = self.findChild(QtWidgets.QCheckBox, 'chk_perblockhmac') 83 | self.chk_perfileuniqueiv = self.findChild(QtWidgets.QCheckBox, 'chk_perfileuniqueiv') 84 | self.chk_chainediv = self.findChild(QtWidgets.QCheckBox, 'chk_chainediv') 85 | self.chk_externaliv = self.findChild(QtWidgets.QCheckBox, 'chk_externaliv') 86 | 87 | self.grp_volumetypes = self.findChild(QtWidgets.QGroupBox, 'grp_volumetypes') 88 | self.grp_encfsoptions = self.findChild(QtWidgets.QGroupBox, 'grp_encfsoptions') 89 | self.grp_gocryptfsoptions = self.findChild(QtWidgets.QGroupBox, 'grp_gocryptfsoptions') 90 | 91 | self.tab_volumetypes = self.findChild(QtWidgets.QTabWidget, 'tab_volumetypes') 92 | self.tab_encfs_options = self.findChild(QtWidgets.QWidget, 'tab_encfs_options') 93 | self.tab_gocryptfs_options = self.findChild(QtWidgets.QWidget, 'tab_gocryptfs_options') 94 | 95 | self.chk_gocryptfs_plaintext = self.findChild(QtWidgets.QCheckBox, 'chk_gocryptfs_plaintext') 96 | self.chk_gocryptfs_aessiv = self.findChild(QtWidgets.QCheckBox, 'chk_gocryptfs_aessiv') 97 | 98 | # populate combo boxes 99 | self.ciphercombo = self.findChild(QtWidgets.QComboBox, ('cmb_ciphers')) 100 | self.ciphercombo.clear() 101 | self.ciphercombo.addItem("AES") 102 | 103 | self.keysizecombo = self.findChild(QtWidgets.QComboBox, ('cmb_keysize')) 104 | self.blocksizecombo = self.findChild(QtWidgets.QComboBox, ('cmb_blocksize')) 105 | self.filenameencodingcombo = self.findChild(QtWidgets.QComboBox, ('cmb_filenameencoding')) 106 | 107 | self.keysizecombo.clear() 108 | self.keysizecombo.addItem("128") 109 | self.keysizecombo.addItem("192") 110 | self.keysizecombo.addItem("256") 111 | 112 | blocksize = 64 113 | self.blocksizecombo.clear() 114 | while (blocksize <= 4096): 115 | self.blocksizecombo.addItem("%d" % blocksize) 116 | blocksize += 16 117 | 118 | self.filenameencodingcombo.clear() 119 | for encodingtype in encfsgui_globals.g_Encodings: 120 | self.filenameencodingcombo.addItem(encodingtype) 121 | 122 | self.SelectProfileBalanced() 123 | # default to GoCryptFS 124 | self.SelectVolumeTypeGoCryptFS() 125 | 126 | 127 | def SelectEncfsFolderClicked(self): 128 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 129 | folderName = str(QtWidgets.QFileDialog.getExistingDirectory(self, "Select Directory", self.txt_encfsfolder.displayText(), QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks)) 130 | if folderName: 131 | self.txt_encfsfolder.setText("%s" % folderName) 132 | return 133 | 134 | def SelectMountFolderClicked(self): 135 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 136 | folderName = str(QtWidgets.QFileDialog.getExistingDirectory(self, "Select Directory", self.txt_mountfolder.displayText(), QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks)) 137 | if folderName: 138 | self.txt_mountfolder.setText("%s" % folderName) 139 | return 140 | 141 | def SelectVolumeTypeEncFS(self): 142 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 143 | #disable the GoCryptFS tab 144 | if self.runmode == 0: 145 | self.grp_encfsoptions.setEnabled(True) 146 | self.grp_gocryptfsoptions.setEnabled(False) 147 | #set EncFS tab in focus 148 | self.tab_encfs_options.setEnabled(True) 149 | self.tab_gocryptfs_options.setEnabled(False) 150 | self.tab_volumetypes.setCurrentWidget(self.tab_encfs_options) 151 | self.tab_encfs_options.show() 152 | #self.chk_mountaslocal.setEnabled(True) 153 | 154 | return 155 | 156 | def SelectVolumeTypeGoCryptFS(self): 157 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 158 | #disable the EncfS tab 159 | if self.runmode == 0: 160 | self.grp_encfsoptions.setEnabled(False) 161 | self.grp_gocryptfsoptions.setEnabled(True) 162 | #set GoCryptFS tab in focus 163 | self.tab_encfs_options.setEnabled(False) 164 | self.tab_gocryptfs_options.setEnabled(True) 165 | self.tab_volumetypes.setCurrentWidget(self.tab_gocryptfs_options) 166 | self.tab_gocryptfs_options.show() 167 | #self.chk_mountaslocal.setEnabled(False) 168 | return 169 | 170 | def SelectProfileCustom(self): 171 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 172 | self.ciphercombo.setEnabled(True) 173 | self.keysizecombo.setEnabled(True) 174 | self.blocksizecombo.setEnabled(True) 175 | self.filenameencodingcombo.setEnabled(True) 176 | self.chk_perblockhmac.setEnabled(True) 177 | self.chk_perfileuniqueiv.setEnabled(True) 178 | self.chk_chainediv.setEnabled(True) 179 | self.chk_externaliv.setEnabled(True) 180 | return 181 | 182 | 183 | def SelectProfileBalanced(self): 184 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 185 | self.ciphercombo.setEnabled(False) 186 | self.keysizecombo.setEnabled(False) 187 | self.blocksizecombo.setEnabled(False) 188 | self.filenameencodingcombo.setEnabled(False) 189 | self.chk_perblockhmac.setEnabled(False) 190 | self.chk_perfileuniqueiv.setEnabled(False) 191 | self.chk_chainediv.setEnabled(False) 192 | self.chk_externaliv.setEnabled(False) 193 | 194 | self.chk_perfileuniqueiv.setChecked(True) 195 | self.chk_externaliv.setChecked(False) 196 | self.chk_chainediv.setChecked(False) 197 | self.chk_perblockhmac.setChecked(False) 198 | 199 | self.ciphercombo.setCurrentText("AES") 200 | self.keysizecombo.setCurrentText("192") 201 | self.blocksizecombo.setCurrentText("2048") 202 | self.filenameencodingcombo.setCurrentText("Stream") 203 | return 204 | 205 | def SelectProfilePerformance(self): 206 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 207 | self.ciphercombo.setEnabled(False) 208 | self.keysizecombo.setEnabled(False) 209 | self.blocksizecombo.setEnabled(False) 210 | self.filenameencodingcombo.setEnabled(False) 211 | self.chk_perblockhmac.setEnabled(False) 212 | self.chk_perfileuniqueiv.setEnabled(False) 213 | self.chk_chainediv.setEnabled(False) 214 | self.chk_externaliv.setEnabled(False) 215 | self.ciphercombo.setCurrentText("AES") 216 | self.keysizecombo.setCurrentText("192") 217 | self.blocksizecombo.setCurrentText("1024") 218 | self.filenameencodingcombo.setCurrentText("Null") 219 | self.chk_perfileuniqueiv.setChecked(False) 220 | self.chk_externaliv.setChecked(False) 221 | self.chk_chainediv.setChecked(False) 222 | self.chk_perblockhmac.setChecked(False) 223 | return 224 | 225 | def SelectProfileSecure(self): 226 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 227 | self.ciphercombo.setEnabled(False) 228 | self.keysizecombo.setEnabled(False) 229 | self.blocksizecombo.setEnabled(False) 230 | self.filenameencodingcombo.setEnabled(False) 231 | self.chk_perblockhmac.setEnabled(False) 232 | self.chk_perfileuniqueiv.setEnabled(False) 233 | self.chk_chainediv.setEnabled(False) 234 | self.chk_externaliv.setEnabled(False) 235 | self.ciphercombo.setCurrentText("AES") 236 | self.keysizecombo.setCurrentText("256") 237 | self.blocksizecombo.setCurrentText("4096") 238 | self.filenameencodingcombo.setCurrentText("Block") 239 | self.chk_perfileuniqueiv.setChecked(True) 240 | self.chk_externaliv.setChecked(True) 241 | self.chk_chainediv.setChecked(True) 242 | self.chk_perblockhmac.setChecked(True) 243 | return 244 | 245 | def SaveButtonClicked(self): 246 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 247 | # sanity check 248 | newvolumename = str(self.txt_volumename.displayText()).strip() 249 | errorfound = False 250 | 251 | if (self.txt_volumename.text().replace(" ","") == ""): 252 | errorfound = True 253 | QtWidgets.QMessageBox.warning(None,"Error checking volume name","Volume name cannot be empty.\n Please enter a unique volume name." ) 254 | 255 | if (self.txt_encfsfolder.text().replace(" ","") == ""): 256 | errorfound = True 257 | QtWidgets.QMessageBox.warning(None,"Error checking encfs/gocryptfs folder path","Please select a valid encfs/gocryptfs folder." ) 258 | else: 259 | if (self.runmode == 0): 260 | # encfs folder must be empty 261 | if (os.listdir(self.txt_encfsfolder.text())): 262 | QtWidgets.QMessageBox.warning(None,"Error checking encfs/gocryptfs folder","Please make sure the new encfs/gocryptfs folder is empty at this point." ) 263 | errorfound = True 264 | 265 | if (self.txt_mountfolder.text().replace(" ","") == ""): 266 | errorfound = True 267 | QtWidgets.QMessageBox.warning(None,"Error checking volume name","Please select a valid mount folder" ) 268 | else: 269 | if (self.runmode == 0): 270 | # encfs folder must be empty 271 | if (os.listdir(self.txt_mountfolder.text())): 272 | QtWidgets.QMessageBox.warning(None,"Error checking mount folder","Please make sure the mount folder is empty at this point." ) 273 | errorfound = True 274 | 275 | # runmode 0 = create 276 | # runmode 1 = add 277 | # runmode 2 = edit 278 | if (self.runmode == 0) or (self.runmode == 1): 279 | if (newvolumename in encfsgui_globals.g_Volumes): 280 | errorfound = True 281 | QtWidgets.QMessageBox.warning(None,"Error checking volume name","Volume name '%s' already exists.\n Please choose a unique volume name." % newvolumename ) 282 | 283 | if (self.runmode == 2): 284 | if (newvolumename != self.origvolumename): 285 | if (newvolumename in encfsgui_globals.g_Volumes): 286 | errorfound = True 287 | QtWidgets.QMessageBox.warning(None,"Error checking volume name","Volume name '%s' already exists.\n Please choose a unique volume name." % newvolumename ) 288 | else: 289 | print_debug("Volume '%s' will be renamed to '%s'" % (self.origvolumename, newvolumename)) 290 | 291 | if self.chk_saveinkeychain.isChecked() and (self.txt_password.text() == "" or self.txt_password2.text() == ""): 292 | errorfound = True 293 | QtWidgets.QMessageBox.warning(None,"Re-enter password","You are trying to rename a volume that had its password saved in keychain.\n\nPlease enter the password again, or disable saving the password in keychain.\n\n" ) 294 | 295 | if (self.chk_saveinkeychain.isChecked() or self.runmode == 0): 296 | if (self.txt_password.text() != self.txt_password2.text()): 297 | errorfound = True 298 | QtWidgets.QMessageBox.warning(None,"Password error","Both passwords do not match" ) 299 | 300 | if not self.chk_saveinkeychain.isChecked() and not self.runmode == 2: 301 | # in edit mode, we can allow empty passwords, providing that password has been saved in keychain already 302 | if (self.txt_password.text() == "" or self.txt_password2.text() == ""): 303 | errorfound = True 304 | QtWidgets.QMessageBox.warning(None,"Password error","Password cannot be empty" ) 305 | 306 | if ("'" in self.txt_password.text()): 307 | errorfound = True 308 | QtWidgets.QMessageBox.warning(None,"Password error","Sorry, you're not allowed to use the 'single-tick' character (') in the password" ) 309 | 310 | if ("!" in self.txt_password.text()): 311 | errorfound = True 312 | QtWidgets.QMessageBox.warning(None,"Password error","Sorry, you're not allowed to use the 'exclamation mark' character (!) in the password" ) 313 | 314 | 315 | if not errorfound: 316 | 317 | # save everything 318 | # translate options into object 319 | EncVolumeObj = encfsgui_globals.CEncryptedVolume() 320 | EncVolumeObj.enc_path = str(self.txt_encfsfolder.displayText()).strip() 321 | EncVolumeObj.mount_path = str(self.txt_mountfolder.displayText()).strip() 322 | EncVolumeObj.encfsmountoptions = str(self.txt_encfsmountoptions.displayText()).strip() 323 | 324 | if (self.radio_volumetype_gocryptfs.isChecked()): 325 | EncVolumeObj.enctype = "gocryptfs" 326 | if (self.radio_volumetype_encfs.isChecked()): 327 | EncVolumeObj.enctype = "encfs" 328 | 329 | if (self.chk_mountaslocal.isChecked()): 330 | EncVolumeObj.mountaslocal = "1" 331 | else: 332 | EncVolumeObj.mountaslocal = "0" 333 | 334 | if (self.chk_saveinkeychain.isChecked()): 335 | EncVolumeObj.passwordsaved = "1" 336 | else: 337 | EncVolumeObj.passwordsaved = "0" 338 | 339 | if (self.chk_automount.isChecked()): 340 | EncVolumeObj.automount = "1" 341 | else: 342 | EncVolumeObj.automount = "0" 343 | 344 | if (self.chk_preventautounmount.isChecked()): 345 | EncVolumeObj.preventautounmount = "1" 346 | else: 347 | EncVolumeObj.preventautounmount = "0" 348 | 349 | if (self.chk_accesstoother.isChecked()): 350 | EncVolumeObj.allowother = "1" 351 | else: 352 | EncVolumeObj.allowother = "0" 353 | 354 | if (self.chk_usesudo.isChecked()): 355 | EncVolumeObj.sudo = "1" 356 | else: 357 | EncVolumeObj.sudo = "0" 358 | 359 | # in create mode, first create the actual encrypted folder 360 | # and then add it to the config 361 | 362 | if (self.runmode == 0): 363 | if (self.CreateNewEncryptedVolume()): 364 | encfsgui_globals.appconfig.addVolume(newvolumename, EncVolumeObj) 365 | 366 | # add mode 367 | if (self.runmode == 1): 368 | encfsgui_globals.appconfig.addVolume(newvolumename, EncVolumeObj) 369 | 370 | # in edit mode, remove previous volume from volume list 371 | # and add new one 372 | if (self.runmode == 2): 373 | encfsgui_globals.appconfig.delVolume(self.origvolumename) 374 | # only remove password if a new is set, to avoid that a simple edit would clear the saved password 375 | if self.chk_saveinkeychain.isChecked() and not self.txt_password.text() == "" and not self.txt_password2 == "": 376 | encfsgui_helper.RemovePasswordFromKeyChain(self.origvolumename) 377 | 378 | encfsgui_globals.appconfig.addVolume(newvolumename, EncVolumeObj) 379 | 380 | # remove old password 381 | if not self.chk_saveinkeychain.isChecked(): 382 | encfsgui_helper.RemovePasswordFromKeyChain(newvolumename) 383 | # save new password 384 | if (self.chk_saveinkeychain.isChecked() and self.txt_password.text() != ""): 385 | encfsgui_helper.RemovePasswordFromKeyChain(newvolumename) 386 | encfsgui_helper.SavePasswordInKeyChain(newvolumename, self.txt_password.text()) 387 | # and close the dialog 388 | self.close() 389 | return 390 | 391 | 392 | def CancelButtonClicked(self): 393 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 394 | self.close() 395 | return 396 | 397 | def CreateNewEncryptedVolume(self): 398 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 399 | msgBox = QtWidgets.QMessageBox() 400 | msgBox.setIcon(QMessageBox.Question) 401 | msgBox.setWindowTitle("Are you sure?") 402 | msgboxtext = "" 403 | if self.radio_volumetype_encfs.isChecked(): 404 | msgboxtext = "Are you sure you would like to create a new encfs volume '%s' at '%s' ?" % (self.txt_volumename.text(), self.txt_encfsfolder.text()) 405 | if self.radio_volumetype_gocryptfs.isChecked(): 406 | msgboxtext = "Are you sure you would like to create a new gocryptfs volume '%s' at '%s' ?" % (self.txt_volumename.text(), self.txt_encfsfolder.text()) 407 | 408 | msgBox.setText(msgboxtext) 409 | msgBox.setStandardButtons(QtWidgets.QMessageBox.No) 410 | msgBox.addButton(QtWidgets.QMessageBox.Yes) 411 | msgBox.show() 412 | 413 | encfolderfound = False 414 | 415 | if (msgBox.exec_() == QtWidgets.QMessageBox.Yes): 416 | 417 | # encfs 418 | 419 | if self.radio_volumetype_encfs.isChecked(): 420 | 421 | cipheralgos = {} 422 | cipheralgos["AES"] = 1 423 | cipheralgos["Blowfish"] = 2 424 | cipheralgos["Camilla"] = 3 425 | 426 | encfsversion = encfsgui_helper.getEncFSVersion() 427 | selectedalgo = 1 428 | if not encfsversion.startswith("1.9."): 429 | # number selection 430 | selectedalgo = str(cipheralgos[self.ciphercombo.currentText()]) 431 | else: 432 | # selectedalgo = "%s" % self.ciphercombo.currentText() 433 | selectedalgo = str(cipheralgos[self.ciphercombo.currentText()]) 434 | 435 | fileencodings = {} 436 | encodingindex = 1 437 | selectedfileencoding = "" 438 | for encodingtype in encfsgui_globals.g_Encodings: 439 | fileencodings[encodingtype] = encodingindex 440 | encodingindex += 1 441 | encfsgui_helper.print_debug("File encodings: %s" % fileencodings) 442 | if not encfsversion.startswith("1.9."): 443 | # number selection 444 | selectedfileencoding = str(fileencodings[self.filenameencodingcombo.currentText()]) 445 | else: 446 | selectedfileencoding = str(fileencodings[self.filenameencodingcombo.currentText()]) 447 | #selectedfileencoding = "%s" % self.filenameencodingcombo.currentText() 448 | encfsgui_helper.print_debug("Encoding selected: %s" % selectedfileencoding) 449 | 450 | # create expect script 451 | scriptcontents = encfsgui_helper.getExpectScriptContents("encfs", False) 452 | # replace variables in the script 453 | scriptcontents = scriptcontents.replace("$ENCFSBIN", encfsgui_globals.g_Settings["encfspath"]) 454 | scriptcontents = scriptcontents.replace("$ENCPATH", self.txt_encfsfolder.text()) 455 | scriptcontents = scriptcontents.replace("$MOUNTPATH", self.txt_mountfolder.text()) 456 | scriptcontents = scriptcontents.replace("$CIPHERALGO", selectedalgo) 457 | scriptcontents = scriptcontents.replace("$CIPHERKEYSIZE", self.keysizecombo.currentText()) 458 | scriptcontents = scriptcontents.replace("$BLOCKSIZE", self.blocksizecombo.currentText()) 459 | scriptcontents = scriptcontents.replace("$ENCODINGALGO", selectedfileencoding) 460 | 461 | if (self.chk_chainediv.isChecked()): 462 | scriptcontents = scriptcontents.replace("$IVCHAINING","") 463 | else: 464 | scriptcontents = scriptcontents.replace("$IVCHAINING","n") 465 | 466 | if (self.chk_perfileuniqueiv.isChecked()): 467 | scriptcontents = scriptcontents.replace("$PERFILEIV","") 468 | else: 469 | scriptcontents = scriptcontents.replace("$PERFILEIV","n") 470 | 471 | if (self.chk_externaliv.isChecked()): 472 | scriptcontents = scriptcontents.replace("$FILETOIVHEADERCHAINING","y") 473 | else: 474 | scriptcontents = scriptcontents.replace("$FILETOIVHEADERCHAINING","") # n is default here 475 | 476 | if (self.chk_perblockhmac.isChecked()): 477 | scriptcontents = scriptcontents.replace("$BLOCKAUTHCODEHEADERS","y") 478 | else: 479 | scriptcontents = scriptcontents.replace("$BLOCKAUTHCODEHEADERS","") # n is default here 480 | 481 | scriptcontents = scriptcontents.replace("sleep x","expect eof") 482 | 483 | expectfilename = "expect.encfsgui" 484 | # write script to file 485 | scriptfile = open(expectfilename, 'w') 486 | scriptfile.write(scriptcontents) 487 | scriptfile.close() 488 | # run script file 489 | cmdarray = ["expect",expectfilename, self.txt_password.text(), ">/dev/null"] 490 | encfsgui_helper.execOScmdAsync(cmdarray) 491 | self.setEnabled(False) 492 | time.sleep(3) 493 | encfolderfound = False 494 | secondswaited = 1 495 | while not encfolderfound: 496 | if (os.path.exists( self.txt_encfsfolder.text() + "/.encfs6.xml" )): 497 | encfolderfound = True 498 | else: 499 | time.sleep(1) 500 | secondswaited += 1 501 | if (secondswaited > 10): 502 | break 503 | self.setEnabled(True) 504 | 505 | # unmount new volume 506 | cmd = "%s '%s'" % (encfsgui_globals.g_Settings["umountpath"], self.txt_mountfolder.text()) 507 | encfsgui_helper.execOSCmd(cmd) 508 | 509 | if encfolderfound: 510 | QtWidgets.QMessageBox.information(None,"EncFS volume created successfully","EncFS volume was created successfully") 511 | #delete script file again 512 | os.remove(expectfilename) 513 | else: 514 | QtWidgets.QMessageBox.information(None,"Error creating volume","Error while creating EncFS volume") 515 | # keep the expect file for debugging purposes in case debug mode is active 516 | if not encfsgui_globals.debugmode: 517 | os.remove(expectfilename) 518 | 519 | # gocryptfs 520 | 521 | if self.radio_volumetype_gocryptfs.isChecked(): 522 | 523 | # create expect script 524 | 525 | extra_opts = "" 526 | if self.chk_gocryptfs_plaintext.isChecked(): 527 | extra_opts += "-plaintextnames " 528 | if self.chk_gocryptfs_aessiv.isChecked(): 529 | extra_opts += "-aessiv " 530 | 531 | 532 | scriptcontents = encfsgui_helper.getExpectScriptContents("gocryptfs", False) 533 | # replace variables in the script 534 | scriptcontents = scriptcontents.replace("$GOCRYPTFSBIN", encfsgui_globals.g_Settings["gocryptfspath"]) 535 | scriptcontents = scriptcontents.replace("$ENCPATH", self.txt_encfsfolder.text()) 536 | scriptcontents = scriptcontents.replace("$MOUNTPATH", self.txt_mountfolder.text()) 537 | scriptcontents = scriptcontents.replace("$EXTRAOPTS", extra_opts) 538 | 539 | scriptcontents = scriptcontents.replace("sleep x","expect eof") 540 | 541 | expectfilename = "expect.encfsgui" 542 | # write script to file 543 | scriptfile = open(expectfilename, 'w') 544 | scriptfile.write(scriptcontents) 545 | scriptfile.close() 546 | # run script file 547 | cmdarray = ["expect",expectfilename, self.txt_password.text(), ">/dev/null"] 548 | #QtWidgets.QMessageBox.information(None,"expect script ready","expect script ready") 549 | 550 | #encfsgui_helper.execOScmdAsync(cmdarray) 551 | gocryptfslines, retval = encfsgui_helper.execOSCmdRetVal(cmdarray) 552 | 553 | self.setEnabled(False) 554 | time.sleep(3) 555 | encfolderfound = False 556 | secondswaited = 1 557 | while not encfolderfound: 558 | if (os.path.exists( self.txt_encfsfolder.text() + "/gocryptfs.diriv" ) or os.path.exists( self.txt_encfsfolder.text() + "/gocryptfs.conf" )): 559 | encfolderfound = True 560 | else: 561 | time.sleep(1) 562 | secondswaited += 1 563 | if (secondswaited > 10): 564 | break 565 | self.setEnabled(True) 566 | 567 | if encfolderfound: 568 | QtWidgets.QMessageBox.information(None,"GoCryptFS volume created successfully","GoCryptFS volume was created successfully") 569 | masterkeyinfo = "" 570 | takenote = False 571 | for line in gocryptfslines: 572 | if "Your master key" in line: 573 | takenote = True 574 | if takenote: 575 | if "-" in line: 576 | masterkeyinfo += "%s\n" % str(line.replace(" ","").replace("[2m","").replace("[0m","").replace("\x1b","")) 577 | QtWidgets.QMessageBox.information(None,"Your master key","WARNING! This is the only time when you get to see your master key!\n\nYour master key is:\n\n%s\n\nPrint it to a piece of paper and store it in a safe location.\n" % masterkeyinfo) 578 | #delete script file again 579 | os.remove(expectfilename) 580 | else: 581 | QtWidgets.QMessageBox.information(None,"Error creating volume","Error while creating GoCryptFS volume") 582 | # keep the expect file for debugging purposes in case debug mode is active 583 | if not encfsgui_globals.debugmode: 584 | os.remove(expectfilename) 585 | 586 | return encfolderfound 587 | 588 | 589 | def setRunMode(self, mode): 590 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 591 | self.runmode = mode 592 | self.lbl_encfsfolder = self.findChild(QtWidgets.QLabel,'lbl_encfsfolder') 593 | 594 | if mode == 0: 595 | self.setWindowTitle("Create a new encfs/gocryptfs volume") 596 | self.lbl_encfsfolder.setText("Location of new (empty) encfs/gocryptfs folder:") 597 | self.savebutton.setText("Create") 598 | self.grp_encfsoptions.setEnabled(True) 599 | self.grp_gocryptfsoptions.setEnabled(True) 600 | self.tab_volumetypes.setEnabled(True) 601 | 602 | if mode == 1: 603 | self.setWindowTitle("Open/Add an existing encfs/gocryptfs volume") 604 | self.lbl_encfsfolder.setText("Location of existing encfs/gocryptfs folder:") 605 | self.savebutton.setText("Add") 606 | self.grp_encfsoptions.setEnabled(False) 607 | self.grp_gocryptfsoptions.setEnabled(False) 608 | self.tab_volumetypes.setEnabled(False) 609 | 610 | if mode == 2: 611 | self.setWindowTitle("Edit an encfs/gocryptfs volume") 612 | self.lbl_encfsfolder.setText("Location of existing encfs/gocryptfs folder:") 613 | self.savebutton.setText("Save") 614 | self.grp_encfsoptions.setEnabled(False) 615 | self.grp_gocryptfsoptions.setEnabled(False) 616 | self.grp_volumetypes.setEnabled(False) 617 | 618 | #unsupported 619 | if encfsgui_helper.isLinux(): 620 | self.chk_mountaslocal.setEnabled(False) 621 | self.chk_mountaslocal.setChecked(False) 622 | 623 | if encfsgui_helper.isWindows(): 624 | self.chk_usesudo.setEnabled(False) 625 | self.chk_usesudo.setChecked(False) 626 | 627 | return 628 | 629 | def getRunMode(self): 630 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 631 | encfsgui_helper.print_debug("mode: %d" % self.runmode) 632 | return 633 | 634 | 635 | def PopulateFields(self, volumename): 636 | encfsgui_helper.print_debug("Start %s" % inspect.stack()[0][3]) 637 | if volumename in encfsgui_globals.g_Volumes: 638 | EncVolumeObj = encfsgui_globals.g_Volumes[volumename] 639 | self.txt_volumename.setText(volumename) 640 | self.txt_encfsfolder.setText(EncVolumeObj.enc_path) 641 | self.txt_mountfolder.setText(EncVolumeObj.mount_path) 642 | self.txt_encfsmountoptions.setText(EncVolumeObj.encfsmountoptions) 643 | self.chk_mountaslocal.setChecked(False) 644 | self.chk_automount.setChecked(False) 645 | self.chk_preventautounmount.setChecked(False) 646 | self.chk_accesstoother.setChecked(False) 647 | self.chk_saveinkeychain.setChecked(False) 648 | 649 | if str(EncVolumeObj.mountaslocal) == "1": 650 | self.chk_mountaslocal.setChecked(True) 651 | if str(EncVolumeObj.automount) == "1": 652 | self.chk_automount.setChecked(True) 653 | if str(EncVolumeObj.preventautounmount) == "1": 654 | self.chk_preventautounmount.setChecked(True) 655 | if str(EncVolumeObj.allowother) == "1": 656 | self.chk_accesstoother.setChecked(True) 657 | if str(EncVolumeObj.passwordsaved) == "1": 658 | self.chk_saveinkeychain.setChecked(True) 659 | if str(EncVolumeObj.sudo) == "1": 660 | self.chk_usesudo.setChecked(True) 661 | if str(EncVolumeObj.enctype) == "encfs": 662 | self.radio_volumetype_encfs.setChecked(True) 663 | self.tab_volumetypes.setCurrentWidget(self.tab_encfs_options) 664 | self.chk_mountaslocal.setEnabled(True) 665 | if str(EncVolumeObj.enctype) == "gocryptfs": 666 | self.radio_volumetype_gocryptfs.setChecked(True) 667 | self.tab_volumetypes.setCurrentWidget(self.tab_gocryptfs_options) 668 | #self.chk_mountaslocal.setEnabled(False) 669 | 670 | # overrule, unsupported features 671 | if encfsgui_helper.isLinux(): 672 | self.chk_mountaslocal.setEnabled(False) 673 | self.chk_mountaslocal.setChecked(False) 674 | 675 | if encfsgui_helper.isWindows(): 676 | self.chk_usesudo.setEnabled(False) 677 | self.chk_usesudo.setChecked(False) 678 | 679 | return 680 | --------------------------------------------------------------------------------