├── 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 |
--------------------------------------------------------------------------------