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