|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2017 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a cache for Google Safe Browsing. |
|
8 """ |
|
9 |
|
10 # |
|
11 # Some part of this code were ported from gglsbl.storage and adapted |
|
12 # to QtSql. |
|
13 # |
|
14 # https://github.com/afilipovich/gglsbl |
|
15 # |
|
16 |
|
17 import os |
|
18 |
|
19 from PyQt6.QtCore import ( |
|
20 QObject, QByteArray, QCryptographicHash, QCoreApplication, QEventLoop |
|
21 ) |
|
22 from PyQt6.QtSql import QSql, QSqlDatabase, QSqlQuery |
|
23 |
|
24 from .SafeBrowsingThreatList import ThreatList |
|
25 |
|
26 |
|
27 class SafeBrowsingCache(QObject): |
|
28 """ |
|
29 Class implementing a cache for Google Safe Browsing. |
|
30 """ |
|
31 create_threat_list_stmt = """ |
|
32 CREATE TABLE threat_list |
|
33 (threat_type character varying(128) NOT NULL, |
|
34 platform_type character varying(128) NOT NULL, |
|
35 threat_entry_type character varying(128) NOT NULL, |
|
36 client_state character varying(42), |
|
37 timestamp timestamp without time zone DEFAULT current_timestamp, |
|
38 PRIMARY KEY (threat_type, platform_type, threat_entry_type) |
|
39 ) |
|
40 """ |
|
41 drop_threat_list_stmt = """DROP TABLE IF EXISTS threat_list""" |
|
42 |
|
43 create_full_hashes_stmt = """ |
|
44 CREATE TABLE full_hash |
|
45 (value BLOB NOT NULL, |
|
46 threat_type character varying(128) NOT NULL, |
|
47 platform_type character varying(128) NOT NULL, |
|
48 threat_entry_type character varying(128) NOT NULL, |
|
49 downloaded_at timestamp without time zone DEFAULT current_timestamp, |
|
50 expires_at timestamp without time zone |
|
51 NOT NULL DEFAULT current_timestamp, |
|
52 malware_threat_type varchar(32), |
|
53 PRIMARY KEY (value, threat_type, platform_type, threat_entry_type) |
|
54 ) |
|
55 """ |
|
56 drop_full_hashes_stmt = """DROP TABLE IF EXISTS full_hash""" |
|
57 |
|
58 create_hash_prefix_stmt = """ |
|
59 CREATE TABLE hash_prefix |
|
60 (value BLOB NOT NULL, |
|
61 cue character varying(4) NOT NULL, |
|
62 threat_type character varying(128) NOT NULL, |
|
63 platform_type character varying(128) NOT NULL, |
|
64 threat_entry_type character varying(128) NOT NULL, |
|
65 timestamp timestamp without time zone DEFAULT current_timestamp, |
|
66 negative_expires_at timestamp without time zone |
|
67 NOT NULL DEFAULT current_timestamp, |
|
68 PRIMARY KEY (value, threat_type, platform_type, threat_entry_type), |
|
69 FOREIGN KEY(threat_type, platform_type, threat_entry_type) |
|
70 REFERENCES threat_list(threat_type, platform_type, threat_entry_type) |
|
71 ON DELETE CASCADE |
|
72 ) |
|
73 """ |
|
74 drop_hash_prefix_stmt = """DROP TABLE IF EXISTS hash_prefix""" |
|
75 |
|
76 create_full_hash_cue_idx = """ |
|
77 CREATE INDEX idx_hash_prefix_cue ON hash_prefix (cue) |
|
78 """ |
|
79 drop_full_hash_cue_idx = """DROP INDEX IF EXISTS idx_hash_prefix_cue""" |
|
80 |
|
81 create_full_hash_expires_idx = """ |
|
82 CREATE INDEX idx_full_hash_expires_at ON full_hash (expires_at) |
|
83 """ |
|
84 drop_full_hash_expires_idx = """ |
|
85 DROP INDEX IF EXISTS idx_full_hash_expires_at |
|
86 """ |
|
87 |
|
88 create_full_hash_value_idx = """ |
|
89 CREATE INDEX idx_full_hash_value ON full_hash (value) |
|
90 """ |
|
91 drop_full_hash_value_idx = """DROP INDEX IF EXISTS idx_full_hash_value""" |
|
92 |
|
93 maxProcessEventsTime = 500 |
|
94 |
|
95 def __init__(self, dbPath, parent=None): |
|
96 """ |
|
97 Constructor |
|
98 |
|
99 @param dbPath path to store the cache DB into |
|
100 @type str |
|
101 @param parent reference to the parent object |
|
102 @type QObject |
|
103 """ |
|
104 super().__init__(parent) |
|
105 |
|
106 self.__connectionName = "SafeBrowsingCache" |
|
107 |
|
108 if not os.path.exists(dbPath): |
|
109 os.makedirs(dbPath) |
|
110 |
|
111 self.__dbFileName = os.path.join(dbPath, "SafeBrowsingCache.db") |
|
112 preparationNeeded = not os.path.exists(self.__dbFileName) |
|
113 |
|
114 self.__openCacheDb() |
|
115 if preparationNeeded: |
|
116 self.prepareCacheDb() |
|
117 |
|
118 def close(self): |
|
119 """ |
|
120 Public method to close the database. |
|
121 """ |
|
122 if QSqlDatabase.database(self.__connectionName).isOpen(): |
|
123 QSqlDatabase.database(self.__connectionName).close() |
|
124 QSqlDatabase.removeDatabase(self.__connectionName) |
|
125 |
|
126 def __openCacheDb(self): |
|
127 """ |
|
128 Private method to open the cache database. |
|
129 |
|
130 @return flag indicating the open state |
|
131 @rtype bool |
|
132 """ |
|
133 db = QSqlDatabase.database(self.__connectionName, False) |
|
134 if not db.isValid(): |
|
135 # the database connection is a new one |
|
136 db = QSqlDatabase.addDatabase("QSQLITE", self.__connectionName) |
|
137 db.setDatabaseName(self.__dbFileName) |
|
138 opened = db.open() |
|
139 if not opened: |
|
140 QSqlDatabase.removeDatabase(self.__connectionName) |
|
141 else: |
|
142 opened = True |
|
143 return opened |
|
144 |
|
145 def prepareCacheDb(self): |
|
146 """ |
|
147 Public method to prepare the cache database. |
|
148 """ |
|
149 db = QSqlDatabase.database(self.__connectionName) |
|
150 db.transaction() |
|
151 try: |
|
152 query = QSqlQuery(db) |
|
153 # step 1: drop old tables |
|
154 query.exec(self.drop_threat_list_stmt) |
|
155 query.exec(self.drop_full_hashes_stmt) |
|
156 query.exec(self.drop_hash_prefix_stmt) |
|
157 # step 2: drop old indices |
|
158 query.exec(self.drop_full_hash_cue_idx) |
|
159 query.exec(self.drop_full_hash_expires_idx) |
|
160 query.exec(self.drop_full_hash_value_idx) |
|
161 # step 3: create tables |
|
162 query.exec(self.create_threat_list_stmt) |
|
163 query.exec(self.create_full_hashes_stmt) |
|
164 query.exec(self.create_hash_prefix_stmt) |
|
165 # step 4: create indices |
|
166 query.exec(self.create_full_hash_cue_idx) |
|
167 query.exec(self.create_full_hash_expires_idx) |
|
168 query.exec(self.create_full_hash_value_idx) |
|
169 finally: |
|
170 del query |
|
171 db.commit() |
|
172 |
|
173 def lookupFullHashes(self, hashValues): |
|
174 """ |
|
175 Public method to get a list of threat lists and expiration flag |
|
176 for the given hashes if a hash is blacklisted. |
|
177 |
|
178 @param hashValues list of hash values to look up |
|
179 @type list of bytes |
|
180 @return list of tuples containing the threat list info and the |
|
181 expiration flag |
|
182 @rtype list of tuple of (ThreatList, bool) |
|
183 """ |
|
184 queryStr = """ |
|
185 SELECT threat_type, platform_type, threat_entry_type, |
|
186 expires_at < current_timestamp AS has_expired |
|
187 FROM full_hash WHERE value IN ({0}) |
|
188 """ |
|
189 output = [] |
|
190 |
|
191 db = QSqlDatabase.database(self.__connectionName) |
|
192 if db.isOpen(): |
|
193 db.transaction() |
|
194 try: |
|
195 query = QSqlQuery(db) |
|
196 query.prepare( |
|
197 queryStr.format(",".join(["?"] * len(hashValues)))) |
|
198 for hashValue in hashValues: |
|
199 query.addBindValue( |
|
200 QByteArray(hashValue), |
|
201 QSql.ParamTypeFlag.In | QSql.ParamTypeFlag.Binary) |
|
202 |
|
203 query.exec() |
|
204 |
|
205 while query.next(): # __IGNORE_WARNING_M523__ |
|
206 threatType = query.value(0) |
|
207 platformType = query.value(1) |
|
208 threatEntryType = query.value(2) |
|
209 hasExpired = bool(query.value(3)) |
|
210 threatList = ThreatList(threatType, platformType, |
|
211 threatEntryType) |
|
212 output.append((threatList, hasExpired)) |
|
213 QCoreApplication.processEvents( |
|
214 QEventLoop.ProcessEventsFlag.AllEvents, |
|
215 self.maxProcessEventsTime) |
|
216 del query |
|
217 finally: |
|
218 db.commit() |
|
219 |
|
220 return output |
|
221 |
|
222 def lookupHashPrefix(self, prefixes): |
|
223 """ |
|
224 Public method to look up hash prefixes in the local cache. |
|
225 |
|
226 @param prefixes list of hash prefixes to look up |
|
227 @type list of bytes |
|
228 @return list of tuples containing the threat list, full hash and |
|
229 negative cache expiration flag |
|
230 @rtype list of tuple of (ThreatList, bytes, bool) |
|
231 """ |
|
232 queryStr = """ |
|
233 SELECT value,threat_type,platform_type,threat_entry_type, |
|
234 negative_expires_at < current_timestamp AS negative_cache_expired |
|
235 FROM hash_prefix WHERE cue IN ({0}) |
|
236 """ |
|
237 output = [] |
|
238 |
|
239 db = QSqlDatabase.database(self.__connectionName) |
|
240 if db.isOpen(): |
|
241 db.transaction() |
|
242 try: |
|
243 query = QSqlQuery(db) |
|
244 query.prepare( |
|
245 queryStr.format(",".join(["?"] * len(prefixes)))) |
|
246 for prefix in prefixes: |
|
247 query.addBindValue(prefix) |
|
248 |
|
249 query.exec() |
|
250 |
|
251 while query.next(): # __IGNORE_WARNING_M523__ |
|
252 fullHash = bytes(query.value(0)) |
|
253 threatType = query.value(1) |
|
254 platformType = query.value(2) |
|
255 threatEntryType = query.value(3) |
|
256 negativeCacheExpired = bool(query.value(4)) |
|
257 threatList = ThreatList(threatType, platformType, |
|
258 threatEntryType) |
|
259 output.append((threatList, fullHash, negativeCacheExpired)) |
|
260 QCoreApplication.processEvents( |
|
261 QEventLoop.ProcessEventsFlag.AllEvents, |
|
262 self.maxProcessEventsTime) |
|
263 del query |
|
264 finally: |
|
265 db.commit() |
|
266 |
|
267 return output |
|
268 |
|
269 def storeFullHash(self, threatList, hashValue, cacheDuration, |
|
270 malwareThreatType): |
|
271 """ |
|
272 Public method to store full hash data in the cache database. |
|
273 |
|
274 @param threatList threat list info object |
|
275 @type ThreatList |
|
276 @param hashValue hash to be stored |
|
277 @type bytes |
|
278 @param cacheDuration duration the data should remain in the cache |
|
279 @type int or float |
|
280 @param malwareThreatType threat type of the malware |
|
281 @type str |
|
282 """ |
|
283 insertQueryStr = """ |
|
284 INSERT OR IGNORE INTO full_hash |
|
285 (value, threat_type, platform_type, threat_entry_type, |
|
286 malware_threat_type, downloaded_at) |
|
287 VALUES |
|
288 (?, ?, ?, ?, ?, current_timestamp) |
|
289 """ |
|
290 updateQueryStr = ( |
|
291 """ |
|
292 UPDATE full_hash SET |
|
293 expires_at=datetime(current_timestamp, '+{0} SECONDS') |
|
294 WHERE value=? AND threat_type=? AND platform_type=? AND |
|
295 threat_entry_type=? |
|
296 """.format(int(cacheDuration)) |
|
297 ) |
|
298 |
|
299 db = QSqlDatabase.database(self.__connectionName) |
|
300 if db.isOpen(): |
|
301 db.transaction() |
|
302 try: |
|
303 query = QSqlQuery(db) |
|
304 query.prepare(insertQueryStr) |
|
305 query.addBindValue( |
|
306 QByteArray(hashValue), |
|
307 QSql.ParamTypeFlag.In | QSql.ParamTypeFlag.Binary) |
|
308 query.addBindValue(threatList.threatType) |
|
309 query.addBindValue(threatList.platformType) |
|
310 query.addBindValue(threatList.threatEntryType) |
|
311 query.addBindValue(malwareThreatType) |
|
312 query.exec() |
|
313 del query |
|
314 |
|
315 query = QSqlQuery(db) |
|
316 query.prepare(updateQueryStr) |
|
317 query.addBindValue( |
|
318 QByteArray(hashValue), |
|
319 QSql.ParamTypeFlag.In | QSql.ParamTypeFlag.Binary) |
|
320 query.addBindValue(threatList.threatType) |
|
321 query.addBindValue(threatList.platformType) |
|
322 query.addBindValue(threatList.threatEntryType) |
|
323 query.exec() |
|
324 del query |
|
325 finally: |
|
326 db.commit() |
|
327 |
|
328 def deleteHashPrefixList(self, threatList): |
|
329 """ |
|
330 Public method to delete hash prefixes for a given threat list. |
|
331 |
|
332 @param threatList threat list info object |
|
333 @type ThreatList |
|
334 """ |
|
335 queryStr = """ |
|
336 DELETE FROM hash_prefix |
|
337 WHERE threat_type=? AND platform_type=? AND threat_entry_type=? |
|
338 """ |
|
339 |
|
340 db = QSqlDatabase.database(self.__connectionName) |
|
341 if db.isOpen(): |
|
342 db.transaction() |
|
343 try: |
|
344 query = QSqlQuery(db) |
|
345 query.prepare(queryStr) |
|
346 query.addBindValue(threatList.threatType) |
|
347 query.addBindValue(threatList.platformType) |
|
348 query.addBindValue(threatList.threatEntryType) |
|
349 query.exec() |
|
350 del query |
|
351 finally: |
|
352 db.commit() |
|
353 |
|
354 def cleanupFullHashes(self, keepExpiredFor=43200): |
|
355 """ |
|
356 Public method to clean up full hash entries expired more than the |
|
357 given time. |
|
358 |
|
359 @param keepExpiredFor time period in seconds of entries to be expired |
|
360 @type int or float |
|
361 """ |
|
362 queryStr = ( |
|
363 """ |
|
364 DELETE FROM full_hash |
|
365 WHERE expires_at=datetime(current_timestamp, '{0} SECONDS') |
|
366 """.format(int(keepExpiredFor)) |
|
367 ) |
|
368 |
|
369 db = QSqlDatabase.database(self.__connectionName) |
|
370 if db.isOpen(): |
|
371 db.transaction() |
|
372 try: |
|
373 query = QSqlQuery(db) |
|
374 query.prepare(queryStr) |
|
375 query.exec() |
|
376 del query |
|
377 finally: |
|
378 db.commit() |
|
379 |
|
380 def updateHashPrefixExpiration(self, threatList, hashPrefix, |
|
381 negativeCacheDuration): |
|
382 """ |
|
383 Public method to update the hash prefix expiration time. |
|
384 |
|
385 @param threatList threat list info object |
|
386 @type ThreatList |
|
387 @param hashPrefix hash prefix |
|
388 @type bytes |
|
389 @param negativeCacheDuration time in seconds the entry should remain |
|
390 in the cache |
|
391 @type int or float |
|
392 """ |
|
393 queryStr = ( |
|
394 """ |
|
395 UPDATE hash_prefix |
|
396 SET negative_expires_at=datetime(current_timestamp, '+{0} SECONDS') |
|
397 WHERE value=? AND threat_type=? AND platform_type=? AND |
|
398 threat_entry_type=? |
|
399 """.format(int(negativeCacheDuration)) |
|
400 ) |
|
401 |
|
402 db = QSqlDatabase.database(self.__connectionName) |
|
403 if db.isOpen(): |
|
404 db.transaction() |
|
405 try: |
|
406 query = QSqlQuery(db) |
|
407 query.prepare(queryStr) |
|
408 query.addBindValue( |
|
409 QByteArray(hashPrefix), |
|
410 QSql.ParamTypeFlag.In | QSql.ParamTypeFlag.Binary) |
|
411 query.addBindValue(threatList.threatType) |
|
412 query.addBindValue(threatList.platformType) |
|
413 query.addBindValue(threatList.threatEntryType) |
|
414 query.exec() |
|
415 del query |
|
416 finally: |
|
417 db.commit() |
|
418 |
|
419 def getThreatLists(self): |
|
420 """ |
|
421 Public method to get the available threat lists. |
|
422 |
|
423 @return list of available threat lists |
|
424 @rtype list of tuples of (ThreatList, str) |
|
425 """ |
|
426 queryStr = """ |
|
427 SELECT threat_type,platform_type,threat_entry_type,client_state |
|
428 FROM threat_list |
|
429 """ |
|
430 output = [] |
|
431 |
|
432 db = QSqlDatabase.database(self.__connectionName) |
|
433 if db.isOpen(): |
|
434 db.transaction() |
|
435 try: |
|
436 query = QSqlQuery(db) |
|
437 query.prepare(queryStr) |
|
438 |
|
439 query.exec() |
|
440 |
|
441 while query.next(): # __IGNORE_WARNING_M523__ |
|
442 threatType = query.value(0) |
|
443 platformType = query.value(1) |
|
444 threatEntryType = query.value(2) |
|
445 clientState = query.value(3) |
|
446 threatList = ThreatList(threatType, platformType, |
|
447 threatEntryType) |
|
448 output.append((threatList, clientState)) |
|
449 QCoreApplication.processEvents( |
|
450 QEventLoop.ProcessEventsFlag.AllEvents, |
|
451 self.maxProcessEventsTime) |
|
452 del query |
|
453 finally: |
|
454 db.commit() |
|
455 |
|
456 return output |
|
457 |
|
458 def addThreatList(self, threatList): |
|
459 """ |
|
460 Public method to add a threat list to the cache. |
|
461 |
|
462 @param threatList threat list to be added |
|
463 @type ThreatList |
|
464 """ |
|
465 queryStr = """ |
|
466 INSERT OR IGNORE INTO threat_list |
|
467 (threat_type, platform_type, threat_entry_type, timestamp) |
|
468 VALUES (?, ?, ?, current_timestamp) |
|
469 """ |
|
470 |
|
471 db = QSqlDatabase.database(self.__connectionName) |
|
472 if db.isOpen(): |
|
473 db.transaction() |
|
474 try: |
|
475 query = QSqlQuery(db) |
|
476 query.prepare(queryStr) |
|
477 query.addBindValue(threatList.threatType) |
|
478 query.addBindValue(threatList.platformType) |
|
479 query.addBindValue(threatList.threatEntryType) |
|
480 query.exec() |
|
481 del query |
|
482 finally: |
|
483 db.commit() |
|
484 |
|
485 def deleteThreatList(self, threatList): |
|
486 """ |
|
487 Public method to delete a threat list from the cache. |
|
488 |
|
489 @param threatList threat list to be deleted |
|
490 @type ThreatList |
|
491 """ |
|
492 queryStr = """ |
|
493 DELETE FROM threat_list |
|
494 WHERE threat_type=? AND platform_type=? AND threat_entry_type=? |
|
495 """ |
|
496 |
|
497 db = QSqlDatabase.database(self.__connectionName) |
|
498 if db.isOpen(): |
|
499 db.transaction() |
|
500 try: |
|
501 query = QSqlQuery(db) |
|
502 query.prepare(queryStr) |
|
503 query.addBindValue(threatList.threatType) |
|
504 query.addBindValue(threatList.platformType) |
|
505 query.addBindValue(threatList.threatEntryType) |
|
506 query.exec() |
|
507 del query |
|
508 finally: |
|
509 db.commit() |
|
510 |
|
511 def updateThreatListClientState(self, threatList, clientState): |
|
512 """ |
|
513 Public method to update the client state of a threat list. |
|
514 |
|
515 @param threatList threat list to update the client state for |
|
516 @type ThreatList |
|
517 @param clientState new client state |
|
518 @type str |
|
519 """ |
|
520 queryStr = """ |
|
521 UPDATE threat_list SET timestamp=current_timestamp, client_state=? |
|
522 WHERE threat_type=? AND platform_type=? AND threat_entry_type=? |
|
523 """ |
|
524 |
|
525 db = QSqlDatabase.database(self.__connectionName) |
|
526 if db.isOpen(): |
|
527 db.transaction() |
|
528 try: |
|
529 query = QSqlQuery(db) |
|
530 query.prepare(queryStr) |
|
531 query.addBindValue(clientState) |
|
532 query.addBindValue(threatList.threatType) |
|
533 query.addBindValue(threatList.platformType) |
|
534 query.addBindValue(threatList.threatEntryType) |
|
535 query.exec() |
|
536 del query |
|
537 finally: |
|
538 db.commit() |
|
539 |
|
540 def hashPrefixListChecksum(self, threatList): |
|
541 """ |
|
542 Public method to calculate the SHA256 checksum for an alphabetically |
|
543 sorted concatenated list of hash prefixes. |
|
544 |
|
545 @param threatList threat list to calculate checksum for |
|
546 @type ThreatList |
|
547 @return SHA256 checksum |
|
548 @rtype bytes |
|
549 """ |
|
550 queryStr = """ |
|
551 SELECT value FROM hash_prefix |
|
552 WHERE threat_type=? AND platform_type=? AND threat_entry_type=? |
|
553 ORDER BY value |
|
554 """ |
|
555 checksum = None |
|
556 |
|
557 db = QSqlDatabase.database(self.__connectionName) |
|
558 if db.isOpen(): |
|
559 db.transaction() |
|
560 sha256Hash = QCryptographicHash( |
|
561 QCryptographicHash.Algorithm.Sha256) |
|
562 try: |
|
563 query = QSqlQuery(db) |
|
564 query.prepare(queryStr) |
|
565 query.addBindValue(threatList.threatType) |
|
566 query.addBindValue(threatList.platformType) |
|
567 query.addBindValue(threatList.threatEntryType) |
|
568 |
|
569 query.exec() |
|
570 |
|
571 while query.next(): # __IGNORE_WARNING_M523__ |
|
572 sha256Hash.addData(query.value(0)) |
|
573 QCoreApplication.processEvents( |
|
574 QEventLoop.ProcessEventsFlag.AllEvents, |
|
575 self.maxProcessEventsTime) |
|
576 del query |
|
577 finally: |
|
578 db.commit() |
|
579 |
|
580 checksum = bytes(sha256Hash.result()) |
|
581 |
|
582 return checksum |
|
583 |
|
584 def populateHashPrefixList(self, threatList, prefixes): |
|
585 """ |
|
586 Public method to populate the hash prefixes for a threat list. |
|
587 |
|
588 @param threatList threat list of the hash prefixes |
|
589 @type ThreatList |
|
590 @param prefixes list of hash prefixes to be inserted |
|
591 @type HashPrefixList |
|
592 """ |
|
593 queryStr = """ |
|
594 INSERT INTO hash_prefix |
|
595 (value, cue, threat_type, platform_type, threat_entry_type, |
|
596 timestamp) |
|
597 VALUES (?, ?, ?, ?, ?, current_timestamp) |
|
598 """ |
|
599 |
|
600 db = QSqlDatabase.database(self.__connectionName) |
|
601 if db.isOpen(): |
|
602 db.transaction() |
|
603 try: |
|
604 for prefix in prefixes: |
|
605 query = QSqlQuery(db) |
|
606 query.prepare(queryStr) |
|
607 query.addBindValue( |
|
608 QByteArray(prefix), |
|
609 QSql.ParamTypeFlag.In | QSql.ParamTypeFlag.Binary) |
|
610 query.addBindValue(prefix[:4].hex()) |
|
611 query.addBindValue(threatList.threatType) |
|
612 query.addBindValue(threatList.platformType) |
|
613 query.addBindValue(threatList.threatEntryType) |
|
614 query.exec() |
|
615 del query |
|
616 QCoreApplication.processEvents( |
|
617 QEventLoop.ProcessEventsFlag.AllEvents, |
|
618 self.maxProcessEventsTime) |
|
619 finally: |
|
620 db.commit() |
|
621 |
|
622 def getHashPrefixValuesToRemove(self, threatList, indexes): |
|
623 """ |
|
624 Public method to get the hash prefix values to be removed from the |
|
625 cache. |
|
626 |
|
627 @param threatList threat list to remove prefixes from |
|
628 @type ThreatList |
|
629 @param indexes list of indexes of prefixes to be removed |
|
630 @type list of int |
|
631 @return list of hash prefixes to be removed |
|
632 @rtype list of bytes |
|
633 """ |
|
634 queryStr = """ |
|
635 SELECT value FROM hash_prefix |
|
636 WHERE threat_type=? AND platform_type=? AND threat_entry_type=? |
|
637 ORDER BY value |
|
638 """ |
|
639 indexes = set(indexes) |
|
640 output = [] |
|
641 |
|
642 db = QSqlDatabase.database(self.__connectionName) |
|
643 if db.isOpen(): |
|
644 db.transaction() |
|
645 try: |
|
646 query = QSqlQuery(db) |
|
647 query.prepare(queryStr) |
|
648 query.addBindValue(threatList.threatType) |
|
649 query.addBindValue(threatList.platformType) |
|
650 query.addBindValue(threatList.threatEntryType) |
|
651 |
|
652 query.exec() |
|
653 |
|
654 index = 0 |
|
655 while query.next(): # __IGNORE_WARNING_M523__ |
|
656 if index in indexes: |
|
657 prefix = bytes(query.value(0)) |
|
658 output.append(prefix) |
|
659 index += 1 |
|
660 QCoreApplication.processEvents( |
|
661 QEventLoop.ProcessEventsFlag.AllEvents, |
|
662 self.maxProcessEventsTime) |
|
663 del query |
|
664 finally: |
|
665 db.commit() |
|
666 |
|
667 return output |
|
668 |
|
669 def removeHashPrefixIndices(self, threatList, indexes): |
|
670 """ |
|
671 Public method to remove hash prefixes from the cache. |
|
672 |
|
673 @param threatList threat list to delete hash prefixes of |
|
674 @type ThreatList |
|
675 @param indexes list of indexes of prefixes to be removed |
|
676 @type list of int |
|
677 """ |
|
678 queryStr = ( |
|
679 """ |
|
680 DELETE FROM hash_prefix |
|
681 WHERE threat_type=? AND platform_type=? AND |
|
682 threat_entry_type=? AND value IN ({0}) |
|
683 """ |
|
684 ) |
|
685 batchSize = 40 |
|
686 |
|
687 prefixesToRemove = self.getHashPrefixValuesToRemove( |
|
688 threatList, indexes) |
|
689 if prefixesToRemove: |
|
690 db = QSqlDatabase.database(self.__connectionName) |
|
691 if db.isOpen(): |
|
692 db.transaction() |
|
693 try: |
|
694 for index in range(0, len(prefixesToRemove), batchSize): |
|
695 removeBatch = prefixesToRemove[ |
|
696 index:(index + batchSize) |
|
697 ] |
|
698 |
|
699 query = QSqlQuery(db) |
|
700 query.prepare( |
|
701 queryStr.format(",".join(["?"] * len(removeBatch))) |
|
702 ) |
|
703 query.addBindValue(threatList.threatType) |
|
704 query.addBindValue(threatList.platformType) |
|
705 query.addBindValue(threatList.threatEntryType) |
|
706 for prefix in removeBatch: |
|
707 query.addBindValue( |
|
708 QByteArray(prefix), |
|
709 QSql.ParamTypeFlag.In | |
|
710 QSql.ParamTypeFlag.Binary) |
|
711 query.exec() |
|
712 del query |
|
713 QCoreApplication.processEvents( |
|
714 QEventLoop.ProcessEventsFlag.AllEvents, |
|
715 self.maxProcessEventsTime) |
|
716 finally: |
|
717 db.commit() |
|
718 |
|
719 # |
|
720 # eflag: noqa = S608 |