7 Module implementing the password manager. |
7 Module implementing the password manager. |
8 """ |
8 """ |
9 |
9 |
10 import os |
10 import os |
11 |
11 |
12 from PyQt4.QtCore import pyqtSignal, QObject, QByteArray, QUrl, QCoreApplication |
12 from PyQt4.QtCore import pyqtSignal, QObject, QByteArray, QUrl, QCoreApplication, \ |
|
13 QXmlStreamReader |
13 from PyQt4.QtGui import QProgressDialog, QApplication |
14 from PyQt4.QtGui import QProgressDialog, QApplication |
14 from PyQt4.QtNetwork import QNetworkRequest |
15 from PyQt4.QtNetwork import QNetworkRequest |
15 from PyQt4.QtWebKit import QWebSettings, QWebPage |
16 from PyQt4.QtWebKit import QWebSettings, QWebPage |
16 |
17 |
17 from E5Gui import E5MessageBox |
18 from E5Gui import E5MessageBox |
18 |
19 |
19 from Helpviewer.JavaScriptResources import parseForms_js |
20 from Helpviewer.JavaScriptResources import parseForms_js |
|
21 |
|
22 from .LoginForm import LoginForm |
|
23 from .PasswordWriter import PasswordWriter |
|
24 from .PasswordReader import PasswordReader |
20 |
25 |
21 from Utilities.AutoSaver import AutoSaver |
26 from Utilities.AutoSaver import AutoSaver |
22 import Utilities |
27 import Utilities |
23 import Utilities.crypto |
28 import Utilities.crypto |
24 import Preferences |
29 import Preferences |
25 |
|
26 |
|
27 class LoginForm(object): |
|
28 """ |
|
29 Class implementing a data structure for login forms. |
|
30 """ |
|
31 def __init__(self): |
|
32 """ |
|
33 Constructor |
|
34 """ |
|
35 self.url = QUrl() |
|
36 self.name = "" |
|
37 self.hasAPassword = False |
|
38 self.elements = [] # list of tuples of element name and value |
|
39 # (string, string) |
|
40 self.elementTypes = {} # dict of element name as key and type as value |
|
41 |
|
42 def isValid(self): |
|
43 """ |
|
44 Public method to test for validity. |
|
45 |
|
46 @return flag indicating a valid form (boolean) |
|
47 """ |
|
48 return len(self.elements) > 0 |
|
49 |
|
50 def load(self, data): |
|
51 """ |
|
52 Public method to load the form data from a file. |
|
53 |
|
54 @param data list of strings to load data from (list of strings) |
|
55 @return flag indicating success (boolean) |
|
56 """ |
|
57 self.url = QUrl(data[0]) |
|
58 self.name = data[1] |
|
59 self.hasAPassword = data[2] == "True" |
|
60 for element in data[3:]: |
|
61 name, value = element.split(" = ", 1) |
|
62 self.elements.append((name, value)) |
|
63 |
|
64 def save(self, f): |
|
65 """ |
|
66 Public method to save the form data to a file. |
|
67 |
|
68 @param f file or file like object open for writing |
|
69 @return flag indicating success (booelan) |
|
70 """ |
|
71 f.write("{0}\n".format(self.url.toString())) |
|
72 f.write("{0}\n".format(self.name)) |
|
73 f.write("{0}\n".format(self.hasAPassword)) |
|
74 for element in self.elements: |
|
75 f.write("{0} = {1}\n".format(element[0], element[1])) |
|
76 |
30 |
77 |
31 |
78 class PasswordManager(QObject): |
32 class PasswordManager(QObject): |
79 """ |
33 """ |
80 Class implementing the password manager. |
34 Class implementing the password manager. |
172 """ |
126 """ |
173 Public method to get the file name of the passwords file. |
127 Public method to get the file name of the passwords file. |
174 |
128 |
175 @return name of the passwords file (string) |
129 @return name of the passwords file (string) |
176 """ |
130 """ |
177 return os.path.join(Utilities.getConfigDir(), "browser", "logins") |
131 return os.path.join(Utilities.getConfigDir(), "browser", "logins.xml") |
178 |
132 |
179 def save(self): |
133 def save(self): |
180 """ |
134 """ |
181 Public slot to save the login entries to disk. |
135 Public slot to save the login entries to disk. |
182 """ |
136 """ |
183 if not self.__loaded: |
137 if not self.__loaded: |
184 return |
138 return |
185 |
139 |
186 loginFile = self.getFileName() |
140 loginFile = self.getFileName() |
187 try: |
141 writer = PasswordWriter() |
188 f = open(loginFile, "w", encoding="utf-8") |
142 if not writer.write(loginFile, self.__logins, self.__loginForms, self.__never): |
189 for key, login in list(self.__logins.items()): |
|
190 f.write("{0}\n".format(key)) |
|
191 f.write("{0}\n".format(login[0])) |
|
192 f.write("{0}\n".format(login[1])) |
|
193 f.write("{0}\n".format(self.SEPARATOR)) |
|
194 if self.__loginForms: |
|
195 f.write("{0}\n".format(self.FORMS)) |
|
196 for key, form in list(self.__loginForms.items()): |
|
197 f.write("{0}\n".format(key)) |
|
198 form.save(f) |
|
199 f.write("{0}\n".format(self.SEPARATOR)) |
|
200 if self.__never: |
|
201 f.write("{0}\n".format(self.NEVER)) |
|
202 for key in self.__never: |
|
203 f.write("{0}\n".format(key)) |
|
204 f.close() |
|
205 self.passwordsSaved.emit() |
|
206 except IOError as err: |
|
207 E5MessageBox.critical(None, |
143 E5MessageBox.critical(None, |
208 self.trUtf8("Saving login data"), |
144 self.trUtf8("Saving login data"), |
209 self.trUtf8("""<p>Login data could not be saved to <b>{0}</b></p>""" |
145 self.trUtf8("""<p>Login data could not be saved to <b>{0}</b></p>""" |
210 """<p>Reason: {1}</p>""").format(loginFile, str(err))) |
146 ).format(loginFile)) |
211 return |
147 else: |
|
148 self.passwordsSaved.emit() |
212 |
149 |
213 def __load(self): |
150 def __load(self): |
214 """ |
151 """ |
215 Private method to load the saved login credentials. |
152 Private method to load the saved login credentials. |
216 """ |
153 """ |
217 loginFile = self.getFileName() |
154 loginFile = self.getFileName() |
|
155 if not os.path.exists(loginFile): |
|
156 self.__loadNonXml(os.path.splitext(loginFile)[0]) |
|
157 else: |
|
158 reader = PasswordReader() |
|
159 self.__logins, self.__loginForms, self.__never = reader.read(loginFile) |
|
160 if reader.error() != QXmlStreamReader.NoError: |
|
161 E5MessageBox.warning(None, |
|
162 self.trUtf8("Loading login data"), |
|
163 self.trUtf8("""Error when loading login data on""" |
|
164 """ line {0}, column {1}:\n{2}""")\ |
|
165 .format(reader.lineNumber(), |
|
166 reader.columnNumber(), |
|
167 reader.errorString())) |
|
168 pass |
|
169 |
|
170 self.__loaded = True |
|
171 |
|
172 def __loadNonXml(self, loginFile): |
|
173 """ |
|
174 Private method to load non-XML password files. |
|
175 |
|
176 This method is to convert from the old, non-XML format to the new |
|
177 XML based format. |
|
178 |
|
179 @param loginFile name of the non-XML password file (string) |
|
180 """ |
218 if os.path.exists(loginFile): |
181 if os.path.exists(loginFile): |
219 try: |
182 try: |
220 f = open(loginFile, "r", encoding="utf-8") |
183 f = open(loginFile, "r", encoding="utf-8") |
221 lines = f.read() |
184 lines = f.read() |
222 f.close() |
185 f.close() |