38 synchronization (string) |
38 synchronization (string) |
39 @signal syncFinished(type_, done, download) emitted after a |
39 @signal syncFinished(type_, done, download) emitted after a |
40 synchronization has finished (string one of "bookmarks", "history", |
40 synchronization has finished (string one of "bookmarks", "history", |
41 "passwords", "useragents" or "speeddial", boolean, boolean) |
41 "passwords", "useragents" or "speeddial", boolean, boolean) |
42 """ |
42 """ |
|
43 |
43 syncStatus = pyqtSignal(str, str) |
44 syncStatus = pyqtSignal(str, str) |
44 syncError = pyqtSignal(str) |
45 syncError = pyqtSignal(str) |
45 syncMessage = pyqtSignal(str) |
46 syncMessage = pyqtSignal(str) |
46 syncFinished = pyqtSignal(str, bool, bool) |
47 syncFinished = pyqtSignal(str, bool, bool) |
47 |
48 |
48 def __init__(self, parent=None): |
49 def __init__(self, parent=None): |
49 """ |
50 """ |
50 Constructor |
51 Constructor |
51 |
52 |
52 @param parent reference to the parent object (QObject) |
53 @param parent reference to the parent object (QObject) |
53 """ |
54 """ |
54 super().__init__(parent) |
55 super().__init__(parent) |
55 |
56 |
56 self.__state = "idle" |
57 self.__state = "idle" |
57 self.__forceUpload = False |
58 self.__forceUpload = False |
58 self.__connected = False |
59 self.__connected = False |
59 |
60 |
60 self.__remoteFilesFound = {} |
61 self.__remoteFilesFound = {} |
61 |
62 |
62 def initialLoadAndCheck(self, forceUpload): |
63 def initialLoadAndCheck(self, forceUpload): |
63 """ |
64 """ |
64 Public method to do the initial check. |
65 Public method to do the initial check. |
65 |
66 |
66 @param forceUpload flag indicating a forced upload of the files |
67 @param forceUpload flag indicating a forced upload of the files |
67 (boolean) |
68 (boolean) |
68 """ |
69 """ |
69 if not Preferences.getWebBrowser("SyncEnabled"): |
70 if not Preferences.getWebBrowser("SyncEnabled"): |
70 return |
71 return |
71 |
72 |
72 self.__state = "initializing" |
73 self.__state = "initializing" |
73 self.__forceUpload = forceUpload |
74 self.__forceUpload = forceUpload |
74 |
75 |
75 self.__dirLineParser = FtpDirLineParser() |
76 self.__dirLineParser = FtpDirLineParser() |
76 self.__remoteFilesFound = {} |
77 self.__remoteFilesFound = {} |
77 |
78 |
78 self.__idleTimer = QTimer(self) |
79 self.__idleTimer = QTimer(self) |
79 self.__idleTimer.setInterval( |
80 self.__idleTimer.setInterval( |
80 Preferences.getWebBrowser("SyncFtpIdleTimeout") * 1000) |
81 Preferences.getWebBrowser("SyncFtpIdleTimeout") * 1000 |
|
82 ) |
81 self.__idleTimer.timeout.connect(self.__idleTimeout) |
83 self.__idleTimer.timeout.connect(self.__idleTimeout) |
82 |
84 |
83 self.__ftp = EricFtp() |
85 self.__ftp = EricFtp() |
84 |
86 |
85 # do proxy setup |
87 # do proxy setup |
86 proxyType = ( |
88 proxyType = ( |
87 EricFtpProxyType.NO_PROXY |
89 EricFtpProxyType.NO_PROXY |
88 if not Preferences.getUI("UseProxy") else |
90 if not Preferences.getUI("UseProxy") |
89 Preferences.getUI("ProxyType/Ftp") |
91 else Preferences.getUI("ProxyType/Ftp") |
90 ) |
92 ) |
91 if proxyType != EricFtpProxyType.NO_PROXY: |
93 if proxyType != EricFtpProxyType.NO_PROXY: |
92 self.__ftp.setProxy( |
94 self.__ftp.setProxy( |
93 proxyType, |
95 proxyType, |
94 Preferences.getUI("ProxyHost/Ftp"), |
96 Preferences.getUI("ProxyHost/Ftp"), |
95 Preferences.getUI("ProxyPort/Ftp")) |
97 Preferences.getUI("ProxyPort/Ftp"), |
|
98 ) |
96 if proxyType != EricFtpProxyType.NON_AUTHORIZING: |
99 if proxyType != EricFtpProxyType.NON_AUTHORIZING: |
97 self.__ftp.setProxyAuthentication( |
100 self.__ftp.setProxyAuthentication( |
98 Preferences.getUI("ProxyUser/Ftp"), |
101 Preferences.getUI("ProxyUser/Ftp"), |
99 Preferences.getUI("ProxyPassword/Ftp"), |
102 Preferences.getUI("ProxyPassword/Ftp"), |
100 Preferences.getUI("ProxyAccount/Ftp")) |
103 Preferences.getUI("ProxyAccount/Ftp"), |
101 |
104 ) |
|
105 |
102 QTimer.singleShot(0, self.__doFtpCommands) |
106 QTimer.singleShot(0, self.__doFtpCommands) |
103 |
107 |
104 def __doFtpCommands(self): |
108 def __doFtpCommands(self): |
105 """ |
109 """ |
106 Private slot executing the sequence of FTP commands. |
110 Private slot executing the sequence of FTP commands. |
107 """ |
111 """ |
108 try: |
112 try: |
113 self.__initialSync() |
117 self.__initialSync() |
114 self.__state = "idle" |
118 self.__state = "idle" |
115 self.__idleTimer.start() |
119 self.__idleTimer.start() |
116 except (ftplib.all_errors + (EricFtpProxyError,)) as err: |
120 except (ftplib.all_errors + (EricFtpProxyError,)) as err: |
117 self.syncError.emit(str(err)) |
121 self.syncError.emit(str(err)) |
118 |
122 |
119 def __connectAndLogin(self): |
123 def __connectAndLogin(self): |
120 """ |
124 """ |
121 Private method to connect to the FTP server and log in. |
125 Private method to connect to the FTP server and log in. |
122 |
126 |
123 @return flag indicating a successful log in (boolean) |
127 @return flag indicating a successful log in (boolean) |
124 """ |
128 """ |
125 self.__ftp.connect( |
129 self.__ftp.connect( |
126 Preferences.getWebBrowser("SyncFtpServer"), |
130 Preferences.getWebBrowser("SyncFtpServer"), |
127 Preferences.getWebBrowser("SyncFtpPort"), |
131 Preferences.getWebBrowser("SyncFtpPort"), |
128 timeout=5) |
132 timeout=5, |
|
133 ) |
129 self.__ftp.login( |
134 self.__ftp.login( |
130 Preferences.getWebBrowser("SyncFtpUser"), |
135 Preferences.getWebBrowser("SyncFtpUser"), |
131 Preferences.getWebBrowser("SyncFtpPassword")) |
136 Preferences.getWebBrowser("SyncFtpPassword"), |
|
137 ) |
132 self.__connected = True |
138 self.__connected = True |
133 return True |
139 return True |
134 |
140 |
135 def __changeToStore(self): |
141 def __changeToStore(self): |
136 """ |
142 """ |
137 Private slot to change to the storage directory. |
143 Private slot to change to the storage directory. |
138 |
144 |
139 This action will create the storage path on the server, if it |
145 This action will create the storage path on the server, if it |
140 does not exist. Upon return, the current directory of the server |
146 does not exist. Upon return, the current directory of the server |
141 is the sync directory. |
147 is the sync directory. |
142 """ |
148 """ |
143 storePathList = Preferences.getWebBrowser("SyncFtpPath").replace( |
149 storePathList = ( |
144 "\\", "/").split("/") |
150 Preferences.getWebBrowser("SyncFtpPath").replace("\\", "/").split("/") |
|
151 ) |
145 if storePathList[0] == "": |
152 if storePathList[0] == "": |
146 storePathList.pop(0) |
153 storePathList.pop(0) |
147 while storePathList: |
154 while storePathList: |
148 path = storePathList[0] |
155 path = storePathList[0] |
149 try: |
156 try: |
194 self.syncStatus.emit(type_, self._messages[type_]["RemoteExists"]) |
201 self.syncStatus.emit(type_, self._messages[type_]["RemoteExists"]) |
195 buffer = io.BytesIO() |
202 buffer = io.BytesIO() |
196 try: |
203 try: |
197 self.__ftp.retrbinary( |
204 self.__ftp.retrbinary( |
198 "RETR {0}".format(self._remoteFiles[type_]), |
205 "RETR {0}".format(self._remoteFiles[type_]), |
199 lambda x: self.__downloadFileCallback(buffer, x)) |
206 lambda x: self.__downloadFileCallback(buffer, x), |
|
207 ) |
200 ok, error = self.writeFile( |
208 ok, error = self.writeFile( |
201 QByteArray(buffer.getvalue()), fileName, type_, timestamp) |
209 QByteArray(buffer.getvalue()), fileName, type_, timestamp |
|
210 ) |
202 if not ok: |
211 if not ok: |
203 self.syncStatus.emit(type_, error) |
212 self.syncStatus.emit(type_, error) |
204 self.syncFinished.emit(type_, ok, True) |
213 self.syncFinished.emit(type_, ok, True) |
205 except ftplib.all_errors as err: |
214 except ftplib.all_errors as err: |
206 self.syncStatus.emit(type_, str(err)) |
215 self.syncStatus.emit(type_, str(err)) |
207 self.syncFinished.emit(type_, False, True) |
216 self.syncFinished.emit(type_, False, True) |
208 |
217 |
209 def __downloadFileCallback(self, buffer, data): |
218 def __downloadFileCallback(self, buffer, data): |
210 """ |
219 """ |
211 Private method receiving the downloaded data. |
220 Private method receiving the downloaded data. |
212 |
221 |
213 @param buffer reference to the buffer (io.BytesIO) |
222 @param buffer reference to the buffer (io.BytesIO) |
214 @param data byte string to store in the buffer (bytes) |
223 @param data byte string to store in the buffer (bytes) |
215 @return number of bytes written to the buffer (integer) |
224 @return number of bytes written to the buffer (integer) |
216 """ |
225 """ |
217 res = buffer.write(data) |
226 res = buffer.write(data) |
218 QCoreApplication.processEvents() |
227 QCoreApplication.processEvents() |
219 return res |
228 return res |
220 |
229 |
221 def __uploadFile(self, type_, fileName): |
230 def __uploadFile(self, type_, fileName): |
222 """ |
231 """ |
223 Private method to upload the given file. |
232 Private method to upload the given file. |
224 |
233 |
225 @param type_ type of the synchronization event (string one |
234 @param type_ type of the synchronization event (string one |
226 of "bookmarks", "history", "passwords", "useragents" or |
235 of "bookmarks", "history", "passwords", "useragents" or |
227 "speeddial") |
236 "speeddial") |
228 @param fileName name of the file to be uploaded (string) |
237 @param fileName name of the file to be uploaded (string) |
229 @return flag indicating success (boolean) |
238 @return flag indicating success (boolean) |
237 buffer = io.BytesIO(data.data()) |
246 buffer = io.BytesIO(data.data()) |
238 try: |
247 try: |
239 self.__ftp.storbinary( |
248 self.__ftp.storbinary( |
240 "STOR {0}".format(self._remoteFiles[type_]), |
249 "STOR {0}".format(self._remoteFiles[type_]), |
241 buffer, |
250 buffer, |
242 callback=lambda x: QCoreApplication.processEvents()) |
251 callback=lambda x: QCoreApplication.processEvents(), |
|
252 ) |
243 self.syncFinished.emit(type_, True, False) |
253 self.syncFinished.emit(type_, True, False) |
244 res = True |
254 res = True |
245 except ftplib.all_errors as err: |
255 except ftplib.all_errors as err: |
246 self.syncStatus.emit(type_, str(err)) |
256 self.syncStatus.emit(type_, str(err)) |
247 self.syncFinished.emit(type_, False, False) |
257 self.syncFinished.emit(type_, False, False) |
248 return res |
258 return res |
249 |
259 |
250 def __initialSyncFile(self, type_, fileName): |
260 def __initialSyncFile(self, type_, fileName): |
251 """ |
261 """ |
252 Private method to do the initial synchronization of the given file. |
262 Private method to do the initial synchronization of the given file. |
253 |
263 |
254 @param type_ type of the synchronization event (string one |
264 @param type_ type of the synchronization event (string one |
255 of "bookmarks", "history", "passwords", "useragents" or |
265 of "bookmarks", "history", "passwords", "useragents" or |
256 "speeddial") |
266 "speeddial") |
257 @param fileName name of the file to be synchronized (string) |
267 @param fileName name of the file to be synchronized (string) |
258 """ |
268 """ |
259 if ( |
269 if ( |
260 not self.__forceUpload and |
270 not self.__forceUpload |
261 self._remoteFiles[type_] in self.__remoteFilesFound |
271 and self._remoteFiles[type_] in self.__remoteFilesFound |
262 ): |
272 ): |
263 if ( |
273 if ( |
264 not pathlib.Path(fileName).exists() or |
274 not pathlib.Path(fileName).exists() |
265 pathlib.Path(fileName).stat().st_mtime < |
275 or pathlib.Path(fileName).stat().st_mtime |
266 self.__remoteFilesFound[ |
276 < self.__remoteFilesFound[self._remoteFiles[type_].toSecsSinceEpoch()] |
267 self._remoteFiles[type_].toSecsSinceEpoch()] |
|
268 ): |
277 ): |
269 self.__downloadFile( |
278 self.__downloadFile( |
270 type_, fileName, |
279 type_, |
271 self.__remoteFilesFound[self._remoteFiles[type_]] |
280 fileName, |
272 .toTime_t()) |
281 self.__remoteFilesFound[self._remoteFiles[type_]].toTime_t(), |
|
282 ) |
273 else: |
283 else: |
274 self.syncStatus.emit( |
284 self.syncStatus.emit(type_, self.tr("No synchronization required.")) |
275 type_, self.tr("No synchronization required.")) |
|
276 self.syncFinished.emit(type_, True, True) |
285 self.syncFinished.emit(type_, True, True) |
277 else: |
286 else: |
278 if self._remoteFiles[type_] not in self.__remoteFilesFound: |
287 if self._remoteFiles[type_] not in self.__remoteFilesFound: |
279 self.syncStatus.emit( |
288 self.syncStatus.emit(type_, self._messages[type_]["RemoteMissing"]) |
280 type_, self._messages[type_]["RemoteMissing"]) |
|
281 else: |
289 else: |
282 self.syncStatus.emit( |
290 self.syncStatus.emit(type_, self._messages[type_]["LocalNewer"]) |
283 type_, self._messages[type_]["LocalNewer"]) |
|
284 self.__uploadFile(type_, fileName) |
291 self.__uploadFile(type_, fileName) |
285 |
292 |
286 def __initialSync(self): |
293 def __initialSync(self): |
287 """ |
294 """ |
288 Private slot to do the initial synchronization. |
295 Private slot to do the initial synchronization. |
289 """ |
296 """ |
290 # Bookmarks |
297 # Bookmarks |
291 if Preferences.getWebBrowser("SyncBookmarks"): |
298 if Preferences.getWebBrowser("SyncBookmarks"): |
292 self.__initialSyncFile( |
299 self.__initialSyncFile( |
293 "bookmarks", |
300 "bookmarks", WebBrowserWindow.bookmarksManager().getFileName() |
294 WebBrowserWindow.bookmarksManager().getFileName()) |
301 ) |
295 |
302 |
296 # History |
303 # History |
297 if Preferences.getWebBrowser("SyncHistory"): |
304 if Preferences.getWebBrowser("SyncHistory"): |
298 self.__initialSyncFile( |
305 self.__initialSyncFile( |
299 "history", |
306 "history", WebBrowserWindow.historyManager().getFileName() |
300 WebBrowserWindow.historyManager().getFileName()) |
307 ) |
301 |
308 |
302 # Passwords |
309 # Passwords |
303 if Preferences.getWebBrowser("SyncPasswords"): |
310 if Preferences.getWebBrowser("SyncPasswords"): |
304 self.__initialSyncFile( |
311 self.__initialSyncFile( |
305 "passwords", |
312 "passwords", WebBrowserWindow.passwordManager().getFileName() |
306 WebBrowserWindow.passwordManager().getFileName()) |
313 ) |
307 |
314 |
308 # User Agent Settings |
315 # User Agent Settings |
309 if Preferences.getWebBrowser("SyncUserAgents"): |
316 if Preferences.getWebBrowser("SyncUserAgents"): |
310 self.__initialSyncFile( |
317 self.__initialSyncFile( |
311 "useragents", |
318 "useragents", WebBrowserWindow.userAgentsManager().getFileName() |
312 WebBrowserWindow.userAgentsManager().getFileName()) |
319 ) |
313 |
320 |
314 # Speed Dial Settings |
321 # Speed Dial Settings |
315 if Preferences.getWebBrowser("SyncSpeedDial"): |
322 if Preferences.getWebBrowser("SyncSpeedDial"): |
316 self.__initialSyncFile( |
323 self.__initialSyncFile( |
317 "speeddial", |
324 "speeddial", WebBrowserWindow.speedDial().getFileName() |
318 WebBrowserWindow.speedDial().getFileName()) |
325 ) |
319 |
326 |
320 self.__forceUpload = False |
327 self.__forceUpload = False |
321 |
328 |
322 def __syncFile(self, type_, fileName): |
329 def __syncFile(self, type_, fileName): |
323 """ |
330 """ |
324 Private method to synchronize the given file. |
331 Private method to synchronize the given file. |
325 |
332 |
326 @param type_ type of the synchronization event (string one |
333 @param type_ type of the synchronization event (string one |
327 of "bookmarks", "history", "passwords", "useragents" or |
334 of "bookmarks", "history", "passwords", "useragents" or |
328 "speeddial") |
335 "speeddial") |
329 @param fileName name of the file to be synchronized (string) |
336 @param fileName name of the file to be synchronized (string) |
330 """ |
337 """ |
331 if self.__state == "initializing": |
338 if self.__state == "initializing": |
332 return |
339 return |
333 |
340 |
334 # use idle timeout to check, if we are still connected |
341 # use idle timeout to check, if we are still connected |
335 if self.__connected: |
342 if self.__connected: |
336 self.__idleTimeout() |
343 self.__idleTimeout() |
337 if not self.__connected or self.__ftp.sock is None: |
344 if not self.__connected or self.__ftp.sock is None: |
338 ok = self.__connectAndLogin() |
345 ok = self.__connectAndLogin() |
339 if not ok: |
346 if not ok: |
340 self.syncStatus.emit( |
347 self.syncStatus.emit(type_, self.tr("Cannot log in to FTP host.")) |
341 type_, self.tr("Cannot log in to FTP host.")) |
|
342 return |
348 return |
343 |
349 |
344 # upload the changed file |
350 # upload the changed file |
345 self.__state = "uploading" |
351 self.__state = "uploading" |
346 self.syncStatus.emit(type_, self._messages[type_]["Uploading"]) |
352 self.syncStatus.emit(type_, self._messages[type_]["Uploading"]) |
347 if self.__uploadFile(type_, fileName): |
353 if self.__uploadFile(type_, fileName): |
348 self.syncStatus.emit( |
354 self.syncStatus.emit(type_, self.tr("Synchronization finished.")) |
349 type_, self.tr("Synchronization finished.")) |
|
350 self.__state = "idle" |
355 self.__state = "idle" |
351 |
356 |
352 def syncBookmarks(self): |
357 def syncBookmarks(self): |
353 """ |
358 """ |
354 Public method to synchronize the bookmarks. |
359 Public method to synchronize the bookmarks. |
355 """ |
360 """ |
|
361 self.__syncFile("bookmarks", WebBrowserWindow.bookmarksManager().getFileName()) |
|
362 |
|
363 def syncHistory(self): |
|
364 """ |
|
365 Public method to synchronize the history. |
|
366 """ |
|
367 self.__syncFile("history", WebBrowserWindow.historyManager().getFileName()) |
|
368 |
|
369 def syncPasswords(self): |
|
370 """ |
|
371 Public method to synchronize the passwords. |
|
372 """ |
|
373 self.__syncFile("passwords", WebBrowserWindow.passwordManager().getFileName()) |
|
374 |
|
375 def syncUserAgents(self): |
|
376 """ |
|
377 Public method to synchronize the user agents. |
|
378 """ |
356 self.__syncFile( |
379 self.__syncFile( |
357 "bookmarks", |
380 "useragents", WebBrowserWindow.userAgentsManager().getFileName() |
358 WebBrowserWindow.bookmarksManager().getFileName()) |
381 ) |
359 |
382 |
360 def syncHistory(self): |
|
361 """ |
|
362 Public method to synchronize the history. |
|
363 """ |
|
364 self.__syncFile( |
|
365 "history", |
|
366 WebBrowserWindow.historyManager().getFileName()) |
|
367 |
|
368 def syncPasswords(self): |
|
369 """ |
|
370 Public method to synchronize the passwords. |
|
371 """ |
|
372 self.__syncFile( |
|
373 "passwords", |
|
374 WebBrowserWindow.passwordManager().getFileName()) |
|
375 |
|
376 def syncUserAgents(self): |
|
377 """ |
|
378 Public method to synchronize the user agents. |
|
379 """ |
|
380 self.__syncFile( |
|
381 "useragents", |
|
382 WebBrowserWindow.userAgentsManager().getFileName()) |
|
383 |
|
384 def syncSpeedDial(self): |
383 def syncSpeedDial(self): |
385 """ |
384 """ |
386 Public method to synchronize the speed dial data. |
385 Public method to synchronize the speed dial data. |
387 """ |
386 """ |
388 self.__syncFile( |
387 self.__syncFile("speeddial", WebBrowserWindow.speedDial().getFileName()) |
389 "speeddial", |
388 |
390 WebBrowserWindow.speedDial().getFileName()) |
|
391 |
|
392 def shutdown(self): |
389 def shutdown(self): |
393 """ |
390 """ |
394 Public method to shut down the handler. |
391 Public method to shut down the handler. |
395 """ |
392 """ |
396 if self.__idleTimer.isActive(): |
393 if self.__idleTimer.isActive(): |
397 self.__idleTimer.stop() |
394 self.__idleTimer.stop() |
398 |
395 |
399 with contextlib.suppress(ftplib.all_errors): |
396 with contextlib.suppress(ftplib.all_errors): |
400 if self.__connected: |
397 if self.__connected: |
401 self.__ftp.quit() |
398 self.__ftp.quit() |
402 self.__connected = False |
399 self.__connected = False |
403 |
400 |
404 def __idleTimeout(self): |
401 def __idleTimeout(self): |
405 """ |
402 """ |
406 Private slot to prevent a disconnect from the server. |
403 Private slot to prevent a disconnect from the server. |
407 """ |
404 """ |
408 if self.__state == "idle" and self.__connected: |
405 if self.__state == "idle" and self.__connected: |