|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2012 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a synchronization handler using FTP. |
|
8 """ |
|
9 |
|
10 from PyQt4.QtCore import pyqtSignal, QUrl, QFile, QIODevice, QTime, QThread |
|
11 from PyQt4.QtNetwork import QFtp, QNetworkProxyQuery, QNetworkProxy, QNetworkProxyFactory |
|
12 |
|
13 from .SyncHandler import SyncHandler |
|
14 |
|
15 import Helpviewer.HelpWindow |
|
16 |
|
17 import Preferences |
|
18 |
|
19 |
|
20 class FtpSyncHandler(SyncHandler): |
|
21 """ |
|
22 Class implementing a synchronization handler using FTP. |
|
23 |
|
24 @signal syncStatus(type_, done, message) emitted to indicate the synchronization |
|
25 status (string one of "bookmarks", "history", "passwords" or "useragents", |
|
26 boolean, string) |
|
27 @signal syncError(message) emitted for a general error with the error message (string) |
|
28 @signal syncFinished(type_, done, download) emitted after a synchronization has |
|
29 finished (string one of "bookmarks", "history", "passwords" or "useragents", |
|
30 boolean, boolean) |
|
31 """ |
|
32 syncStatus = pyqtSignal(str, bool, str) |
|
33 syncError = pyqtSignal(str) |
|
34 syncFinished = pyqtSignal(str, bool, bool) |
|
35 |
|
36 def __init__(self, parent=None): |
|
37 """ |
|
38 Constructor |
|
39 |
|
40 @param parent reference to the parent object (QObject) |
|
41 """ |
|
42 super().__init__(parent) |
|
43 |
|
44 self.__state = "idle" |
|
45 |
|
46 self.__remoteFiles = { |
|
47 "bookmarks": "Bookmarks", |
|
48 "history": "History", |
|
49 "passwords": "Logins", |
|
50 "useragents": "UserAgentSettings" |
|
51 } |
|
52 self.__remoteFilesFound = [] |
|
53 |
|
54 self.__messages = { |
|
55 "bookmarks": { |
|
56 "RemoteExists": self.trUtf8( |
|
57 "Remote bookmarks file exists! Syncing local copy..."), |
|
58 "RemoteMissing": self.trUtf8( |
|
59 "Remote bookmarks file does NOT exists. Exporting local copy..."), |
|
60 "LocalMissing": self.trUtf8( |
|
61 "Local bookmarks file does NOT exist. Skipping synchronization!"), |
|
62 }, |
|
63 "history": { |
|
64 "RemoteExists": self.trUtf8( |
|
65 "Remote history file exists! Syncing local copy..."), |
|
66 "RemoteMissing": self.trUtf8( |
|
67 "Remote history file does NOT exists. Exporting local copy..."), |
|
68 "LocalMissing": self.trUtf8( |
|
69 "Local history file does NOT exist. Skipping synchronization!"), |
|
70 }, |
|
71 "passwords": { |
|
72 "RemoteExists": self.trUtf8( |
|
73 "Remote logins file exists! Syncing local copy..."), |
|
74 "RemoteMissing": self.trUtf8( |
|
75 "Remote logins file does NOT exists. Exporting local copy..."), |
|
76 "LocalMissing": self.trUtf8( |
|
77 "Local logins file does NOT exist. Skipping synchronization!"), |
|
78 }, |
|
79 "useragents": { |
|
80 "RemoteExists": self.trUtf8( |
|
81 "Remote user agent settings file exists! Syncing local copy..."), |
|
82 "RemoteMissing": self.trUtf8( |
|
83 "Remote user agent settings file does NOT exists." |
|
84 " Exporting local copy..."), |
|
85 "LocalMissing": self.trUtf8( |
|
86 "Local user agent settings file does NOT exist." |
|
87 " Skipping synchronization!"), |
|
88 }, |
|
89 } |
|
90 |
|
91 def initialLoadAndCheck(self): |
|
92 """ |
|
93 Public method to do the initial check. |
|
94 """ |
|
95 if not Preferences.getHelp("SyncEnabled"): |
|
96 return |
|
97 |
|
98 self.__state = "initializing" |
|
99 |
|
100 self.__remoteFilesFound = [] |
|
101 self.__syncIDs = {} |
|
102 |
|
103 self.__ftp = QFtp(self) |
|
104 self.__ftp.commandFinished.connect(self.__commandFinished) |
|
105 self.__ftp.listInfo.connect(self.__checkSyncFiles) |
|
106 |
|
107 # do proxy setup |
|
108 url = QUrl("ftp://{0}:{1}".format( |
|
109 Preferences.getHelp("SyncFtpServer"), |
|
110 Preferences.getHelp("SyncFtpPort") |
|
111 )) |
|
112 query = QNetworkProxyQuery(url) |
|
113 proxyList = QNetworkProxyFactory.proxyForQuery(query) |
|
114 ftpProxy = QNetworkProxy() |
|
115 for proxy in proxyList: |
|
116 if proxy.type() == QNetworkProxy.NoProxy or \ |
|
117 proxy.type() == QNetworkProxy.FtpCachingProxy: |
|
118 ftpProxy = proxy |
|
119 break |
|
120 if ftpProxy.type() == QNetworkProxy.DefaultProxy: |
|
121 self.syncError.emit(self.trUtf8("No suitable proxy found.")) |
|
122 return |
|
123 elif ftpProxy.type() == QNetworkProxy.FtpCachingProxy: |
|
124 self.__ftp.setProxy(ftpProxy.hostName(), ftpProxy.port()) |
|
125 |
|
126 self.__ftp.connectToHost(Preferences.getHelp("SyncFtpServer"), |
|
127 Preferences.getHelp("SyncFtpPort")) |
|
128 self.__ftp.login(Preferences.getHelp("SyncFtpUser"), |
|
129 Preferences.getHelp("SyncFtpPassword")) |
|
130 |
|
131 def __changeToStore(self): |
|
132 """ |
|
133 Private slot to change to the storage directory. |
|
134 |
|
135 This action might cause the storage path to be created on the server. |
|
136 """ |
|
137 self.__storePathList = \ |
|
138 Preferences.getHelp("SyncFtpPath").replace("\\", "/").split("/") |
|
139 if self.__storePathList[0] == "": |
|
140 del self.__storePathList[0] |
|
141 self.__ftp.cd(self.__storePathList[0]) |
|
142 |
|
143 def __commandFinished(self, id, error): |
|
144 """ |
|
145 Private slot handling the end of a command. |
|
146 |
|
147 @param id id of the finished command (integer) |
|
148 @param error flag indicating an error situation (boolean) |
|
149 """ |
|
150 if error: |
|
151 if self.__ftp.currentCommand() in [ |
|
152 QFtp.ConnectToHost, QFtp.Login, QFtp.Mkdir, QFtp.List]: |
|
153 self.syncError.emit(self.__ftp.errorString()) |
|
154 elif self.__ftp.currentCommand() == QFtp.Cd: |
|
155 self.__ftp.mkdir(self.__storePathList[0]) |
|
156 self.__ftp.cd(self.__storePathList[0]) |
|
157 else: |
|
158 if id in self.__syncIDs: |
|
159 self.__syncIDs[id][1].close() |
|
160 self.syncStatus.emit(self.__syncIDs[id][0], False, |
|
161 self.__ftp.errorString()) |
|
162 self.syncFinished.emit(self.__syncIDs[id][0], False, |
|
163 self.__syncIDs[id][2]) |
|
164 del self.__syncIDs[id] |
|
165 else: |
|
166 if self.__ftp.currentCommand() == QFtp.Login: |
|
167 self.__changeToStore() |
|
168 elif self.__ftp.currentCommand() == QFtp.Cd: |
|
169 del self.__storePathList[0] |
|
170 if self.__storePathList: |
|
171 self.__ftp.cd(self.__storePathList[0]) |
|
172 else: |
|
173 self.__storeReached() |
|
174 elif self.__ftp.currentCommand() == QFtp.List: |
|
175 self.__initialSync() |
|
176 else: |
|
177 if id in self.__syncIDs: |
|
178 self.__syncIDs[id][1].close() |
|
179 self.syncFinished.emit(self.__syncIDs[id][0], True, |
|
180 self.__syncIDs[id][2]) |
|
181 del self.__syncIDs[id] |
|
182 |
|
183 def __storeReached(self): |
|
184 """ |
|
185 Private slot executed, when the storage directory was reached. |
|
186 """ |
|
187 self.__ftp.list() |
|
188 |
|
189 def __checkSyncFiles(self, info): |
|
190 """ |
|
191 Private slot called for each entry sent by the FTP list command. |
|
192 |
|
193 @param info info about the entry (QUrlInfo) |
|
194 """ |
|
195 if info.isValid() and info.isFile(): |
|
196 if info.name() in self.__remoteFiles.values(): |
|
197 self.__remoteFilesFound.append(info.name()) |
|
198 |
|
199 def __initialSyncFile(self, type_, fileName): |
|
200 """ |
|
201 Private method to do the initial synchronization of the given file. |
|
202 |
|
203 @param type_ type of the synchronization event (string one |
|
204 of "bookmarks", "history", "passwords" or "useragents") |
|
205 @param fileName name of the file to be synchronized (string) |
|
206 """ |
|
207 f = QFile(fileName) |
|
208 if self.__remoteFiles[type_] in self.__remoteFilesFound: |
|
209 self.syncStatus.emit(type_, True, |
|
210 self.__messages[type_]["RemoteExists"]) |
|
211 f.open(QIODevice.WriteOnly) |
|
212 id = self.__ftp.get(self.__remoteFiles[type_], f) |
|
213 self.__syncIDs[id] = (type_, f, True) |
|
214 else: |
|
215 if f.exists(): |
|
216 self.syncStatus.emit(type_, True, |
|
217 self.__messages[type_]["RemoteMissing"]) |
|
218 f.open(QIODevice.ReadOnly) |
|
219 id = self.__ftp.put(f, self.__remoteFiles[type_]) |
|
220 self.__syncIDs[id] = (type_, f, False) |
|
221 else: |
|
222 self.syncStatus.emit(type_, True, |
|
223 self.__messages[type_]["LocalMissing"]) |
|
224 |
|
225 def __initialSync(self): |
|
226 """ |
|
227 Private slot to do the initial synchronization. |
|
228 """ |
|
229 # Bookmarks |
|
230 if Preferences.getHelp("SyncBookmarks"): |
|
231 self.__initialSyncFile("bookmarks", |
|
232 Helpviewer.HelpWindow.HelpWindow.bookmarksManager().getFileName()) |
|
233 |
|
234 # History |
|
235 if Preferences.getHelp("SyncHistory"): |
|
236 self.__initialSyncFile("history", |
|
237 Helpviewer.HelpWindow.HelpWindow.historyManager().getFileName()) |
|
238 |
|
239 # Passwords |
|
240 if Preferences.getHelp("SyncPasswords"): |
|
241 self.__initialSyncFile("passwords", |
|
242 Helpviewer.HelpWindow.HelpWindow.passwordManager().getFileName()) |
|
243 |
|
244 # User Agent Settings |
|
245 if Preferences.getHelp("SyncUserAgents"): |
|
246 self.__initialSyncFile("useragents", |
|
247 Helpviewer.HelpWindow.HelpWindow.userAgentsManager().getFileName()) |
|
248 |
|
249 def __syncFile(self, type_, fileName): |
|
250 """ |
|
251 Private method to synchronize the given file. |
|
252 |
|
253 @param type_ type of the synchronization event (string one |
|
254 of "bookmarks", "history", "passwords" or "useragents") |
|
255 @param fileName name of the file to be synchronized (string) |
|
256 """ |
|
257 f = QFile(fileName) |
|
258 if f.exists(): |
|
259 f.open(QIODevice.ReadOnly) |
|
260 id = self.__ftp.put(f, self.__remoteFiles[type_]) |
|
261 self.__syncIDs[id] = (type_, f, False) |
|
262 |
|
263 def syncBookmarks(self): |
|
264 """ |
|
265 Public method to synchronize the bookmarks. |
|
266 """ |
|
267 self.__syncFile("bookmarks", |
|
268 Helpviewer.HelpWindow.HelpWindow.bookmarksManager().getFileName()) |
|
269 |
|
270 def syncHistory(self): |
|
271 """ |
|
272 Public method to synchronize the history. |
|
273 """ |
|
274 self.__syncFile("history", |
|
275 Helpviewer.HelpWindow.HelpWindow.historyManager().getFileName()) |
|
276 |
|
277 def syncPasswords(self): |
|
278 """ |
|
279 Public method to synchronize the passwords. |
|
280 """ |
|
281 self.__syncFile("passwords", |
|
282 Helpviewer.HelpWindow.HelpWindow.passwordManager().getFileName()) |
|
283 |
|
284 def syncUserAgents(self): |
|
285 """ |
|
286 Public method to synchronize the user agents. |
|
287 """ |
|
288 self.__syncFile("useragents", |
|
289 Helpviewer.HelpWindow.HelpWindow.userAgentsManager().getFileName()) |
|
290 |
|
291 def shutdown(self): |
|
292 """ |
|
293 Public method to shut down the handler. |
|
294 """ |
|
295 t = QTime.currentTime() |
|
296 t.start() |
|
297 while t.elapsed() < 5000 and self.__ftp.hasPendingCommands(): |
|
298 QThread.msleep(200) |
|
299 if self.__ftp.hasPendingCommands(): |
|
300 self.__ftp.clearPendingCommands() |
|
301 if self.__ftp.currentCommand() != 0: |
|
302 self.__ftp.abort() |