chg: [qrcode] extract qrcode content from images and screenshots + qrcode object + correlation

This commit is contained in:
terrtia 2024-10-01 15:12:15 +02:00
parent a199148ca7
commit 9f45202658
No known key found for this signature in database
GPG key ID: 1E1B1F50D84613D0
28 changed files with 1314 additions and 69 deletions

View file

@ -280,6 +280,8 @@ function launching_scripts {
sleep 0.1 sleep 0.1
screen -S "Script_AIL" -X screen -t "OcrExtractor" bash -c "cd ${AIL_BIN}/modules; ${ENV_PY} ./OcrExtractor.py; read x" screen -S "Script_AIL" -X screen -t "OcrExtractor" bash -c "cd ${AIL_BIN}/modules; ${ENV_PY} ./OcrExtractor.py; read x"
sleep 0.1 sleep 0.1
screen -S "Script_AIL" -X screen -t "QrCodeReader" bash -c "cd ${AIL_BIN}/modules; ${ENV_PY} ./QrCodeReader.py; read x"
sleep 0.1
################################## ##################################
# TRACKERS MODULES # # TRACKERS MODULES #

View file

@ -359,6 +359,7 @@ class Crawler(AbstractModule):
# Create Correlations # Create Correlations
screenshot.add_correlation('item', '', item_id) screenshot.add_correlation('item', '', item_id)
screenshot.add_correlation('domain', '', self.domain.id) screenshot.add_correlation('domain', '', self.domain.id)
self.add_message_to_queue(obj=screenshot, queue='Images')
# HAR # HAR
if self.har: if self.har:
if 'har' in entries and entries.get('har'): if 'har' in entries and entries.get('har'):

View file

@ -17,15 +17,15 @@ r_object = config_loader.get_db_conn("Kvrocks_Objects")
config_loader = None config_loader = None
AIL_OBJECTS = sorted({'chat', 'chat-subchannel', 'chat-thread', 'cookie-name', 'cve', 'cryptocurrency', 'decoded', AIL_OBJECTS = sorted({'chat', 'chat-subchannel', 'chat-thread', 'cookie-name', 'cve', 'cryptocurrency', 'decoded',
'domain', 'etag', 'favicon', 'file-name', 'hhhash', 'domain', 'etag', 'favicon', 'file-name', 'hhhash','item', 'image', 'message', 'ocr', 'pgp',
'item', 'image', 'message', 'ocr', 'pgp', 'screenshot', 'title', 'user-account', 'username'}) 'qrcode', 'screenshot', 'title', 'user-account', 'username'})
AIL_OBJECTS_WITH_SUBTYPES = {'chat', 'chat-subchannel', 'cryptocurrency', 'pgp', 'username', 'user-account'} AIL_OBJECTS_WITH_SUBTYPES = {'chat', 'chat-subchannel', 'cryptocurrency', 'pgp', 'username', 'user-account'}
# TODO by object TYPE ???? # TODO by object TYPE ????
AIL_OBJECTS_CORRELATIONS_DEFAULT = sorted({'chat', 'chat-subchannel', 'chat-thread', 'cve', 'cryptocurrency', 'decoded', AIL_OBJECTS_CORRELATIONS_DEFAULT = sorted({'chat', 'chat-subchannel', 'chat-thread', 'cve', 'cryptocurrency', 'decoded',
'domain', 'favicon', 'file-name', 'domain', 'favicon', 'file-name', 'item', 'image', 'message', 'ocr', 'pgp',
'item', 'image', 'message', 'ocr', 'pgp', 'screenshot', 'title', 'user-account', 'username'}) 'qrcode', 'screenshot', 'title', 'user-account', 'username'})
def get_ail_uuid(): def get_ail_uuid():
ail_uuid = r_serv_db.get('ail:uuid') ail_uuid = r_serv_db.get('ail:uuid')
@ -86,10 +86,10 @@ def get_default_correlation_objects():
return AIL_OBJECTS_CORRELATIONS_DEFAULT return AIL_OBJECTS_CORRELATIONS_DEFAULT
def get_obj_queued(): def get_obj_queued():
return ['item', 'image', 'message', 'ocr'] return ['item', 'image', 'message', 'ocr', 'qrcode']
def get_objects_tracked(): def get_objects_tracked():
return ['decoded', 'item', 'pgp', 'message', 'ocr', 'title'] return ['decoded', 'item', 'pgp', 'message', 'ocr', 'qrcode', 'title']
def get_objects_retro_hunted(): def get_objects_retro_hunted():
return ['decoded', 'item', 'message'] return ['decoded', 'item', 'message']

View file

@ -43,22 +43,23 @@ config_loader = None
CORRELATION_TYPES_BY_OBJ = { CORRELATION_TYPES_BY_OBJ = {
"chat": ["chat-subchannel", "chat-thread", "image", "message", "ocr", "user-account"], # message or direct correlation like cve, bitcoin, ... ??? "chat": ["chat-subchannel", "chat-thread", "image", "message", "ocr", "user-account"], # message or direct correlation like cve, bitcoin, ... ???
"chat-subchannel": ["chat", "chat-thread", "image", "message", "ocr", "user-account"], "chat-subchannel": ["chat", "chat-thread", "image", "message", "ocr", "user-account"],
"chat-thread": ["chat", "chat-subchannel", "image", "message", "ocr", "user-account"], # TODO user account "chat-thread": ["chat", "chat-subchannel", "image", "message", "ocr", "user-account"],
"cookie-name": ["domain"], "cookie-name": ["domain"],
"cryptocurrency": ["domain", "item", "message", "ocr"], "cryptocurrency": ["domain", "item", "message", "ocr", "qrcode"],
"cve": ["domain", "item", "message", "ocr"], "cve": ["domain", "item", "message", "ocr", "qrcode"],
"decoded": ["domain", "item", "message", "ocr"], "decoded": ["domain", "item", "message", "ocr", "qrcode"],
"domain": ["cve", "cookie-name", "cryptocurrency", "decoded", "etag", "favicon", "hhhash", "item", "pgp", "title", "screenshot", "username"], "domain": ["cve", "cookie-name", "cryptocurrency", "decoded", "etag", "favicon", "hhhash", "item", "pgp", "title", "screenshot", "username"],
"etag": ["domain"], "etag": ["domain"],
"favicon": ["domain", "item"], # TODO Decoded "favicon": ["domain", "item"], # TODO Decoded
"file-name": ["chat", "message"], "file-name": ["chat", "message"],
"hhhash": ["domain"], "hhhash": ["domain"],
"image": ["chat", "chat-subchannel", "chat-thread", "message", "ocr", "user-account"], # TODO subchannel + threads ???? "image": ["chat", "chat-subchannel", "chat-thread", "message", "ocr", "qrcode", "user-account"], # TODO subchannel + threads ????
"item": ["cve", "cryptocurrency", "decoded", "domain", "favicon", "pgp", "screenshot", "title", "username"], # chat ??? "item": ["cve", "cryptocurrency", "decoded", "domain", "favicon", "pgp", "screenshot", "title", "username"], # chat ???
"message": ["chat", "chat-subchannel", "chat-thread", "cve", "cryptocurrency", "decoded", "file-name", "image", "ocr", "pgp", "user-account"], "message": ["chat", "chat-subchannel", "chat-thread", "cve", "cryptocurrency", "decoded", "file-name", "image", "ocr", "pgp", "user-account"],
"ocr": ["chat", "chat-subchannel", "chat-thread", "cve", "cryptocurrency", "decoded", "image", "message", "pgp", "user-account"], "ocr": ["chat", "chat-subchannel", "chat-thread", "cve", "cryptocurrency", "decoded", "image", "message", "pgp", "user-account"],
"pgp": ["domain", "item", "message", "ocr"], "pgp": ["domain", "item", "message", "ocr"],
"screenshot": ["domain", "item"], "qrcode": ["chat", "cve", "cryptocurrency", "decoded", "domain", "image", "message", "screenshot"], # "chat-subchannel", "chat-thread" ?????
"screenshot": ["domain", "item", "qrcode"],
"title": ["domain", "item"], "title": ["domain", "item"],
"user-account": ["chat", "chat-subchannel", "chat-thread", "image", "message", "ocr", "username"], "user-account": ["chat", "chat-subchannel", "chat-thread", "image", "message", "ocr", "username"],
"username": ["domain", "item", "message", "user-account"], "username": ["domain", "item", "message", "user-account"],
@ -67,7 +68,9 @@ CORRELATION_TYPES_BY_OBJ = {
def get_obj_correl_types(obj_type): def get_obj_correl_types(obj_type):
return CORRELATION_TYPES_BY_OBJ.get(obj_type) return CORRELATION_TYPES_BY_OBJ.get(obj_type)
def sanityze_obj_correl_types(obj_type, correl_types): def sanityze_obj_correl_types(obj_type, correl_types, sanityze=True):
if not sanityze:
return correl_types
obj_correl_types = get_obj_correl_types(obj_type) obj_correl_types = get_obj_correl_types(obj_type)
if correl_types: if correl_types:
correl_types = set(correl_types).intersection(obj_correl_types) correl_types = set(correl_types).intersection(obj_correl_types)
@ -99,11 +102,11 @@ def get_correlation_by_correl_type(obj_type, subtype, obj_id, correl_type, unpac
else: else:
return correl return correl
def get_correlations(obj_type, subtype, obj_id, filter_types=[], unpack=False): def get_correlations(obj_type, subtype, obj_id, filter_types=[], unpack=False, sanityze=True):
if subtype is None: if subtype is None:
subtype = '' subtype = ''
obj_correlations = {} obj_correlations = {}
filter_types = sanityze_obj_correl_types(obj_type, filter_types) filter_types = sanityze_obj_correl_types(obj_type, filter_types, sanityze=sanityze)
for correl_type in filter_types: for correl_type in filter_types:
obj_correlations[correl_type] = get_correlation_by_correl_type(obj_type, subtype, obj_id, correl_type, obj_correlations[correl_type] = get_correlation_by_correl_type(obj_type, subtype, obj_id, correl_type,
unpack=unpack) unpack=unpack)

163
bin/lib/objects/QrCodes.py Executable file
View file

@ -0,0 +1,163 @@
#!/usr/bin/env python3
# -*-coding:UTF-8 -*
import os
import sys
from hashlib import sha256
from pymisp import MISPObject
sys.path.append(os.environ['AIL_BIN'])
##################################
# Import Project packages
##################################
from lib.objects.abstract_daterange_object import AbstractDaterangeObject, AbstractDaterangeObjects
from lib.ConfigLoader import ConfigLoader
from packages import Date
# from lib.data_retention_engine import update_obj_date, get_obj_date_first
from flask import url_for
config_loader = ConfigLoader()
r_object = config_loader.get_db_conn("Kvrocks_Objects")
r_cache = config_loader.get_redis_conn("Redis_Cache")
baseurl = config_loader.get_config_str("Notifications", "ail_domain")
IMAGE_FOLDER = config_loader.get_files_directory('images')
config_loader = None
# SET x1,y1:x2,y2:x3,y3:x4,y4:extracted_text
class Qrcode(AbstractDaterangeObject):
"""
AIL Message Object. (strings)
"""
def __init__(self, id):
super(Qrcode, self).__init__('qrcode', id)
def get_content(self, r_type='str'):
"""
Returns content
"""
global_id = self.get_global_id()
content = r_cache.get(f'content:{global_id}')
if not content:
content = self._get_field('content')
# Set Cache
if content:
global_id = self.get_global_id()
r_cache.set(f'content:{global_id}', content)
r_cache.expire(f'content:{global_id}', 300)
if r_type == 'str':
return content
elif r_type == 'bytes':
if content:
return content.encode()
def get_date(self): # TODO
return Date.get_today_date_str()
def get_nb_seen(self):
return self.get_nb_correlation('image') + self.get_nb_correlation('screenshot')
def get_source(self): # TODO
"""
Returns source/feeder name
"""
return 'qrcode'
def get_basename(self): # TODO
return 'qrcode'
def get_link(self, flask_context=False):
if flask_context:
url = url_for('correlation.show_correlation', type=self.type, id=self.id)
else:
url = f'{baseurl}/correlation/show?type={self.type}&id={self.id}'
return url
def get_svg_icon(self): # TODO
return {'style': 'fas', 'icon': '\uf029', 'color': 'yellow', 'radius': 5}
def get_misp_object(self): # TODO
pass
# obj = MISPObject('instant-message', standalone=True)
# obj_date = self.get_date()
# if obj_date:
# obj.first_seen = obj_date
# else:
# self.logger.warning(
# f'Export error, None seen {self.type}:{self.subtype}:{self.id}, first={obj_date}')
#
# # obj_attrs = [obj.add_attribute('first-seen', value=obj_date),
# # obj.add_attribute('raw-data', value=self.id, data=self.get_raw_content()),
# # obj.add_attribute('sensor', value=get_ail_uuid())]
# obj_attrs = []
# for obj_attr in obj_attrs:
# for tag in self.get_tags():
# obj_attr.add_tag(tag)
# return obj
# options: set of optional meta fields
def get_meta(self, options=None):
"""
:type options: set
"""
if options is None:
options = set()
meta = self._get_meta(options=options)
meta['tags'] = self.get_tags()
meta['content'] = self.get_content()
# optional meta fields
if 'investigations' in options:
meta['investigations'] = self.get_investigations()
if 'link' in options:
meta['link'] = self.get_link(flask_context=True)
if 'icon' in options:
meta['svg_icon'] = self.get_svg_icon()
return meta
def create(self, content, im_obj, tags=[]):
self._set_field('content', content)
if im_obj.type == 'screenshot':
for date in im_obj.get_dates():
self._add(date, None)
else:
self._copy_from(im_obj.type, im_obj.get_id())
for tag in tags:
self.add_tag(tag)
return self.id
# # WARNING: UNCLEAN DELETE /!\ TEST ONLY /!\
def delete(self):
r_object.delete(f'qrcode:{self.id}')
def create(content, im_obj, tags=[]):
if content:
obj_id = sha256(content.encode()).hexdigest()
obj = Qrcode(obj_id)
if not obj.exists():
obj.create(content, im_obj, tags=tags)
return obj
class Qrcodes(AbstractDaterangeObjects):
"""
Qrcodes Objects
"""
def __init__(self):
super().__init__('qrcode', Qrcode)
def sanitize_id_to_search(self, name_to_search):
return name_to_search # TODO
#### API ####
def api_get_qrcode(obj_id):
obj = Qrcode(obj_id)
if not obj.exists():
return {"status": "error", "reason": "Unknown qrcode"}, 404
meta = obj.get_meta({'content', 'icon', 'link'})
return meta, 200

View file

@ -47,6 +47,22 @@ class Screenshot(AbstractObject):
def exists(self): def exists(self):
return os.path.isfile(self.get_filepath()) return os.path.isfile(self.get_filepath())
def get_last_seen(self):
dates = self.get_dates()
date = 0
for d in dates:
if int(d) > int(date):
date = d
return date
def get_dates(self):
dates = []
for i_id in self.get_correlation('item').get('item'):
if i_id.startswith(':crawled'):
i_id = i_id.split('/', 4)
dates.append(f'{i_id[1]}{i_id[2]}{i_id[3]}')
return dates
def get_link(self, flask_context=False): def get_link(self, flask_context=False):
if flask_context: if flask_context:
url = url_for('correlation.show_correlation', type=self.type, id=self.id) url = url_for('correlation.show_correlation', type=self.type, id=self.id)
@ -116,6 +132,14 @@ def get_all_screenshots():
screenshots.append(screenshot_id) screenshots.append(screenshot_id)
return screenshots return screenshots
def get_screenshots_obj_iterator(filters=[]):
screenshot_dir = os.path.join(os.environ['AIL_HOME'], SCREENSHOT_FOLDER)
for root, dirs, files in os.walk(screenshot_dir):
for file in files:
screenshot_path = f'{root}{file}'
screenshot_id = screenshot_path.replace(SCREENSHOT_FOLDER, '').replace('/', '')[:-4]
yield Screenshot(screenshot_id)
# FIXME STR SIZE LIMIT # FIXME STR SIZE LIMIT
def create_screenshot(content, size_limit=5000000, b64=True, force=False): def create_screenshot(content, size_limit=5000000, b64=True, force=False):
size = (len(content)*3) / 4 size = (len(content)*3) / 4
@ -155,5 +179,6 @@ def search_screenshots_by_name(name_to_search, r_pos=False):
# if __name__ == '__main__': # if __name__ == '__main__':
# name_to_search = '29ba' # obj_id = ''
# print(search_screenshots_by_name(name_to_search)) # obj = Screenshot(obj_id)
# obj.get_last_seen()

View file

@ -132,7 +132,7 @@ class AbstractDaterangeObject(AbstractObject, ABC):
last_seen = r_object.hget(f'meta:{obj_type}:{obj_id}', 'last_seen') last_seen = r_object.hget(f'meta:{obj_type}:{obj_id}', 'last_seen')
if first_seen and last_seen: if first_seen and last_seen:
for date in Date.get_daterange(first_seen, last_seen): for date in Date.get_daterange(first_seen, last_seen):
nb = r_object.zscore(f'{obj_type}:date:{date}', self.id) nb = r_object.zscore(f'{obj_type}:date:{date}', obj_id)
if nb: if nb:
r_object.zincrby(f'{self.type}:date:{date}', nb, self.id) r_object.zincrby(f'{self.type}:date:{date}', nb, self.id)
update_obj_date(first_seen, self.type) update_obj_date(first_seen, self.type)

View file

@ -235,7 +235,7 @@ class AbstractObject(ABC):
""" """
Get object correlation Get object correlation
""" """
return get_correlations(self.type, self.subtype, self.id, filter_types=[obj_type]) return get_correlations(self.type, self.subtype, self.id, filter_types=[obj_type], sanityze=False)
def get_first_correlation(self, obj_type): def get_first_correlation(self, obj_type):
correlation = self.get_correlation(obj_type) correlation = self.get_correlation(obj_type)

View file

@ -36,7 +36,8 @@ from lib.objects import Images
from lib.objects import Messages from lib.objects import Messages
from lib.objects import Ocrs from lib.objects import Ocrs
from lib.objects import Pgps from lib.objects import Pgps
from lib.objects.Screenshots import Screenshot from lib.objects import QrCodes
from lib.objects import Screenshots
from lib.objects import Titles from lib.objects import Titles
from lib.objects import UsersAccount from lib.objects import UsersAccount
from lib.objects import Usernames from lib.objects import Usernames
@ -98,8 +99,10 @@ def get_object(obj_type, subtype, obj_id):
return Messages.Message(obj_id) return Messages.Message(obj_id)
elif obj_type == 'ocr': elif obj_type == 'ocr':
return Ocrs.Ocr(obj_id) return Ocrs.Ocr(obj_id)
elif obj_type == 'qrcode':
return QrCodes.Qrcode(obj_id)
elif obj_type == 'screenshot': elif obj_type == 'screenshot':
return Screenshot(obj_id) return Screenshots.Screenshot(obj_id)
elif obj_type == 'title': elif obj_type == 'title':
return Titles.Title(obj_id) return Titles.Title(obj_id)
else: else:
@ -330,6 +333,8 @@ def obj_iterator(obj_type, filters):
return get_all_decodeds_objects(filters=filters) return get_all_decodeds_objects(filters=filters)
elif obj_type == 'image': elif obj_type == 'image':
return Images.get_all_images_objects(filters=filters) return Images.get_all_images_objects(filters=filters)
elif obj_type == 'screenshot':
return Screenshots.get_screenshots_obj_iterator(filters=filters)
elif obj_type == 'item': elif obj_type == 'item':
return get_all_items_objects(filters=filters) return get_all_items_objects(filters=filters)
elif obj_type == 'pgp': elif obj_type == 'pgp':

View file

@ -89,7 +89,7 @@ class Categ(AbstractModule):
# Search for pattern categories in obj content # Search for pattern categories in obj content
for categ, pattern in self.categ_words: for categ, pattern in self.categ_words:
if obj.type == 'message' or obj.type == 'ocr': if obj.type == 'message' or obj.type == 'ocr' or obj.type == 'qrcode':
self.add_message_to_queue(message='0', queue=categ) self.add_message_to_queue(message='0', queue=categ)
else: else:

View file

@ -133,6 +133,7 @@ class Global(AbstractModule):
self.add_message_to_queue(obj=self.obj, queue='Item') self.add_message_to_queue(obj=self.obj, queue='Item')
elif self.obj.type == 'image': elif self.obj.type == 'image':
self.add_message_to_queue(obj=self.obj, queue='Image', message=message) self.add_message_to_queue(obj=self.obj, queue='Image', message=message)
self.add_message_to_queue(obj=self.obj, queue='Images', message=message)
else: else:
self.logger.critical(f"Empty obj: {self.obj} {message} not processed") self.logger.critical(f"Empty obj: {self.obj} {message} not processed")

View file

@ -135,10 +135,10 @@ class Mail(AbstractModule):
# # TODO: sanitize mails # # TODO: sanitize mails
def compute(self, message): def compute(self, message):
score = message score = message
item = self.get_obj() obj = self.get_obj()
item_date = item.get_date() item_date = item.get_date()
mails = self.regex_findall(self.email_regex, item.id, item.get_content()) mails = self.regex_findall(self.email_regex, obj.id, obj.get_content())
mxdomains_email = {} mxdomains_email = {}
for mail in mails: for mail in mails:
mxdomain = mail.rsplit('@', 1)[1].lower() mxdomain = mail.rsplit('@', 1)[1].lower()

108
bin/modules/QrCodeReader.py Executable file
View file

@ -0,0 +1,108 @@
#!/usr/bin/env python3
# -*-coding:UTF-8 -*
"""
The OcrExtractor Module
======================
"""
##################################
# Import External packages
##################################
import cv2
import os
import sys
sys.path.append(os.environ['AIL_BIN'])
##################################
# Import Project packages
##################################
from modules.abstract_module import AbstractModule
from lib.ConfigLoader import ConfigLoader
from lib.objects import QrCodes
class QrCodeReader(AbstractModule):
"""
QrCodeReader for AIL framework
"""
def __init__(self):
super(QrCodeReader, self).__init__()
# Waiting time in seconds between to message processed
self.pending_seconds = 1
config_loader = ConfigLoader()
self.r_cache = config_loader.get_redis_conn("Redis_Cache")
# Send module state to logs
self.logger.info(f'Module {self.module_name} initialized')
def is_cached(self):
return self.r_cache.exists(f'qrcode:no:{self.obj.type}:{self.obj.id}')
def add_to_cache(self):
self.r_cache.setex(f'qrcode:no:{self.obj.type}:{self.obj.id}', 86400, 0)
def extract_qrcode(self, path):
detector = cv2.QRCodeDetector() # TODO Move me in init ???
image = cv2.imread(path)
# multiple extraction
try:
qr_found, contents, qarray, _ = detector.detectAndDecodeMulti(image)
if qr_found:
return contents
else:
# simple extraction
content, box, _ = detector.detectAndDecode(image)
if content:
return [content]
else:
return []
except cv2.error as e:
self.logger.error(f'{e}, {self.obj.get_global_id()}')
def compute(self, message):
obj = self.get_obj()
if self.is_cached():
return None
if obj.type == 'image':
if self.obj.is_gif():
self.logger.warning(f'Ignoring GIF: {self.obj.id}')
return None
# image - screenshot
path = self.obj.get_filepath()
contents = self.extract_qrcode(path)
if not contents:
# print('no qr code detected')
self.add_to_cache()
return None
for content in contents:
print(content)
qr_code = QrCodes.create(content, self.obj) # copy screenshot + image daterange
qr_code.add(qr_code.get_date(), self.obj)
for obj_type in ['chat', 'domain', 'message']: # TODO ITEM ???
for c_id in self.obj.get_correlation(obj_type).get(obj_type, []):
o_subtype, o_id = c_id.split(':', 1)
qr_code.add_correlation(obj_type, o_subtype, o_id)
tag = 'infoleak:automatic-detection="qrcode"'
self.add_message_to_queue(obj=self.obj, message=tag, queue='Tags')
# TODO only if new ???
self.add_message_to_queue(obj=qr_code, queue='Item')
if __name__ == '__main__':
module = QrCodeReader()
module.run()
# from lib.objects.Images import Image
# module.obj = Image('8a690f4d09509dbfe52a6fb139db500b16b3d5f07e22617944752c4d4885737c')
# module.compute(None)

View file

@ -1,7 +1,7 @@
######## IMPORTERS ######## ######## IMPORTERS ########
[Crawler] [Crawler]
publish = Importers,Tags publish = Importers,Tags,Images
[ZMQModuleImporter] [ZMQModuleImporter]
publish = Importers publish = Importers
@ -29,7 +29,7 @@ publish = Importers,Tags
[Global] [Global]
subscribe = SaveObj subscribe = SaveObj
publish = Item,Image publish = Item,Image,Images
######## ITEM + MESSAGE ######## ######## ITEM + MESSAGE ########
@ -156,7 +156,7 @@ publish = Tags
#[Sync_module] #[Sync_module]
#publish = Sync #publish = Sync
######## IMAGE ######## ######## IMAGE ######## image
[Exif] [Exif]
subscribe = Image subscribe = Image
@ -166,6 +166,12 @@ publish = Tags
subscribe = Image subscribe = Image
publish = Item publish = Item
######## IMAGES ######## images + screenshots
[QrCodeReader]
subscribe = Images
publish = Item,Tags
######## CORE ######## ######## CORE ########
[Tags] [Tags]

View file

@ -31,10 +31,12 @@ from lib.objects import ail_objects
from modules.Languages import Languages from modules.Languages import Languages
from modules.OcrExtractor import OcrExtractor from modules.OcrExtractor import OcrExtractor
from modules.QrCodeReader import QrCodeReader
MODULES = { MODULES = {
'Languages': Languages, 'Languages': Languages,
'OcrExtractor': OcrExtractor 'OcrExtractor': OcrExtractor,
'QrCodeReader': QrCodeReader
} }
@ -68,7 +70,7 @@ if __name__ == "__main__":
obj_type = args.type obj_type = args.type
if not is_object_type(obj_type): if not is_object_type(obj_type):
raise Exception(f'Invalid Object Type: {obj_type}') raise Exception(f'Invalid Object Type: {obj_type}')
if obj_type not in ['image', 'item', 'message']: if obj_type not in ['image', 'item', 'message', 'screenshot']:
raise Exception(f'Currently not supported Object Type: {obj_type}') raise Exception(f'Currently not supported Object Type: {obj_type}')
modulename = args.module modulename = args.module

View file

@ -57,6 +57,7 @@ from blueprints.objects_hhhash import objects_hhhash
from blueprints.chats_explorer import chats_explorer from blueprints.chats_explorer import chats_explorer
from blueprints.objects_image import objects_image from blueprints.objects_image import objects_image
from blueprints.objects_ocr import objects_ocr from blueprints.objects_ocr import objects_ocr
from blueprints.objects_qrcode import objects_qrcode
from blueprints.objects_favicon import objects_favicon from blueprints.objects_favicon import objects_favicon
from blueprints.api_rest import api_rest from blueprints.api_rest import api_rest
@ -140,6 +141,7 @@ app.register_blueprint(objects_hhhash, url_prefix=baseUrl)
app.register_blueprint(chats_explorer, url_prefix=baseUrl) app.register_blueprint(chats_explorer, url_prefix=baseUrl)
app.register_blueprint(objects_image, url_prefix=baseUrl) app.register_blueprint(objects_image, url_prefix=baseUrl)
app.register_blueprint(objects_ocr, url_prefix=baseUrl) app.register_blueprint(objects_ocr, url_prefix=baseUrl)
app.register_blueprint(objects_qrcode, url_prefix=baseUrl)
app.register_blueprint(objects_favicon, url_prefix=baseUrl) app.register_blueprint(objects_favicon, url_prefix=baseUrl)
app.register_blueprint(api_rest, url_prefix=baseUrl) app.register_blueprint(api_rest, url_prefix=baseUrl)

View file

@ -0,0 +1,101 @@
#!/usr/bin/env python3
# -*-coding:UTF-8 -*
'''
Blueprint Flask: crawler splash endpoints: dashboard, onion crawler ...
'''
import json
import os
import sys
from io import BytesIO
from flask import Flask, render_template, jsonify, request, Blueprint, redirect, url_for, Response, abort, send_file, send_from_directory
from flask_login import login_required
# Import Role_Manager
from Role_Manager import login_admin, login_read_only, no_cache
sys.path.append(os.environ['AIL_BIN'])
##################################
# Import Project packages
##################################
from lib import Language
from lib import Tag
from lib.objects import QrCodes
from packages import Date
# ============ BLUEPRINT ============
objects_qrcode = Blueprint('objects_qrcode', __name__, template_folder=os.path.join(os.environ['AIL_FLASK'], 'templates/objects/qrcode'))
# ============ VARIABLES ============
bootstrap_label = ['primary', 'success', 'danger', 'warning', 'info']
def create_json_response(data, status_code):
return Response(json.dumps(data, indent=2, sort_keys=True), mimetype='application/json'), status_code
# ============ FUNCTIONS ============
@objects_qrcode.route("/objects/qrcodes", methods=['GET'])
@login_required
@login_read_only
def objects_qrcodes():
date_from = request.args.get('date_from')
date_to = request.args.get('date_to')
show_objects = request.args.get('show_objects')
date = Date.sanitise_date_range(date_from, date_to)
date_from = date['date_from']
date_to = date['date_to']
if show_objects:
dict_objects = QrCodes.Qrcodes().api_get_meta_by_daterange(date_from, date_to)
else:
dict_objects = {}
return render_template("QrcodeDaterange.html", date_from=date_from, date_to=date_to,
dict_objects=dict_objects, show_objects=show_objects)
@objects_qrcode.route("/objects/qrcodes/post", methods=['POST'])
@login_required
@login_read_only
def objects_qrcodes_post():
date_from = request.form.get('date_from')
date_to = request.form.get('date_to')
show_objects = request.form.get('show_objects')
return redirect(url_for('objects_qrcode.objects_qrcodes', date_from=date_from, date_to=date_to, show_objects=show_objects))
@objects_qrcode.route("/objects/qrcodes/range/json", methods=['GET'])
@login_required
@login_read_only
def objects_qrcodes_range_json():
date_from = request.args.get('date_from')
date_to = request.args.get('date_to')
date = Date.sanitise_date_range(date_from, date_to)
date_from = date['date_from']
date_to = date['date_to']
return jsonify(QrCodes.Qrcodes().api_get_chart_nb_by_daterange(date_from, date_to))
@objects_qrcode.route("/objects/qrcodes", methods=['GET'])
@login_required
@login_read_only
def object_qrcode():
obj_id = request.args.get('id')
meta = QrCodes.api_get_qrcode(obj_id)
if meta[1] != 200:
return create_json_response(meta[0], meta[1])
else:
meta = meta[0]
languages = Language.get_translation_languages()
return render_template("ShowQrcode.html", meta=meta,
bootstrap_label=bootstrap_label,
ail_tags=Tag.get_modal_add_tags(meta['id'], meta['type'], meta['subtype']),
translation_languages=languages, translation_target=target)
# ============= ROUTES ==============

View file

@ -299,7 +299,6 @@ def tags_search_messages():
def tags_search_images(): def tags_search_images():
object_type = 'image' object_type = 'image'
dict_tagged = {"object_type": object_type, "object_name": object_type.title() + "s"} dict_tagged = {"object_type": object_type, "object_name": object_type.title() + "s"}
dict_tagged['date'] = Date.sanitise_date_range('', '', separator='-')
return render_template("tags/search_obj_by_tags.html", bootstrap_label=bootstrap_label, dict_tagged=dict_tagged) return render_template("tags/search_obj_by_tags.html", bootstrap_label=bootstrap_label, dict_tagged=dict_tagged)
@tags_ui.route('/tag/search/ocr') @tags_ui.route('/tag/search/ocr')
@ -308,7 +307,6 @@ def tags_search_images():
def tags_search_ocrs(): def tags_search_ocrs():
object_type = 'ocr' object_type = 'ocr'
dict_tagged = {"object_type": object_type, "object_name": object_type.title() + "s"} dict_tagged = {"object_type": object_type, "object_name": object_type.title() + "s"}
dict_tagged['date'] = Date.sanitise_date_range('', '', separator='-')
return render_template("tags/search_obj_by_tags.html", bootstrap_label=bootstrap_label, dict_tagged=dict_tagged) return render_template("tags/search_obj_by_tags.html", bootstrap_label=bootstrap_label, dict_tagged=dict_tagged)
@tags_ui.route('/tag/search/domain') @tags_ui.route('/tag/search/domain')

View file

@ -132,6 +132,8 @@
{% include 'chats_explorer/card_image.html' %} {% include 'chats_explorer/card_image.html' %}
{% elif dict_object["object_type"] == "ocr" %} {% elif dict_object["object_type"] == "ocr" %}
{% include 'objects/ocr/card_ocr.html' %} {% include 'objects/ocr/card_ocr.html' %}
{% elif dict_object["object_type"] == "qrcode" %}
{% include 'objects/qrcode/card_qrcode.html' %}
{% elif dict_object["object_type"] == "item" %} {% elif dict_object["object_type"] == "item" %}
{% include 'correlation/metadata_card_item.html' %} {% include 'correlation/metadata_card_item.html' %}
{% elif dict_object["object_type"] == "favicon" %} {% elif dict_object["object_type"] == "favicon" %}
@ -319,6 +321,10 @@
<input class="form-check-input" type="checkbox" value="True" id="ocr_Check" name="ocr_Check" {%if "ocr" in dict_object["filter"]%}checked{%endif%}> <input class="form-check-input" type="checkbox" value="True" id="ocr_Check" name="ocr_Check" {%if "ocr" in dict_object["filter"]%}checked{%endif%}>
<label class="form-check-label" for="ocr_Check">OCR</label> <label class="form-check-label" for="ocr_Check">OCR</label>
</div> </div>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="True" id="qrcode_Check" name="qrcode_Check" {%if "qrcode" in dict_object["filter"]%}checked{%endif%}>
<label class="form-check-label" for="qrcode_Check">Qrcode</label>
</div>
<hr> <hr>
<div class="form-check"> <div class="form-check">

View file

@ -140,9 +140,13 @@
<input class="custom-control-input" type="checkbox" name="message_obj" id="message_obj" {% if not dict_tracker['filters'] or 'message' in dict_tracker['filters'] %}checked=""{% endif %}> <input class="custom-control-input" type="checkbox" name="message_obj" id="message_obj" {% if not dict_tracker['filters'] or 'message' in dict_tracker['filters'] %}checked=""{% endif %}>
<label class="custom-control-label" for="message_obj"><i class="fas fa-comment-dots"></i>&nbsp;Message <i class="fas fa-info-circle text-info" data-toggle="tooltip" data-placement="right" title="Messages from Chats"></i></label> <label class="custom-control-label" for="message_obj"><i class="fas fa-comment-dots"></i>&nbsp;Message <i class="fas fa-info-circle text-info" data-toggle="tooltip" data-placement="right" title="Messages from Chats"></i></label>
</div> </div>
<div class="custom-control custom-switch mt-1">
<input class="custom-control-input" type="checkbox" name="qrcode_obj" id="qrcode_obj" {% if not dict_tracker['filters'] or 'qrcode' in dict_tracker['filters'] %}checked=""{% endif %}>
<label class="custom-control-label" for="qrcode_obj"><i class="fas fa-qrcode"></i>&nbsp;Qrcode <i class="fas fa-info-circle text-info" data-toggle="tooltip" data-placement="right" title="Qcodes Extracted from Images ans Screenshots"></i></label>
</div>
<div class="custom-control custom-switch mt-1"> <div class="custom-control custom-switch mt-1">
<input class="custom-control-input" type="checkbox" name="ocr_obj" id="ocr_obj" {% if not dict_tracker['filters'] or 'ocr' in dict_tracker['filters'] %}checked=""{% endif %}> <input class="custom-control-input" type="checkbox" name="ocr_obj" id="ocr_obj" {% if not dict_tracker['filters'] or 'ocr' in dict_tracker['filters'] %}checked=""{% endif %}>
<label class="custom-control-label" for="ocr_obj"><i class="fas fa-comment-dots"></i>&nbsp;OCR <i class="fas fa-expand text-info" data-toggle="tooltip" data-placement="right" title="Text extracted from Images"></i></label> <label class="custom-control-label" for="ocr_obj"><i class="fas fa-expand"></i>&nbsp;OCR <i class="fas fa-info-circle text-info" data-toggle="tooltip" data-placement="right" title="Text extracted from Images"></i></label>
</div> </div>
{# <div class="custom-control custom-switch mt-1">#} {# <div class="custom-control custom-switch mt-1">#}

View file

@ -0,0 +1,167 @@
<link href="{{ url_for('static', filename='css/tags.css') }}" rel="stylesheet" type="text/css" />
<script src="{{ url_for('static', filename='js/tags.js') }}"></script>
{% with modal_add_tags=ail_tags %}
{% include 'modals/add_tags.html' %}
{% endwith %}
{% include 'modals/edit_tag.html' %}
<style>
.object_image {
filter: blur(5px);
}
</style>
<div class="card my-1">
<div class="card-header">
<h4 class="text-secondary">{{ meta["id"] }} :</h4>
<ul class="list-group mb-2">
<li class="list-group-item py-0">
<table class="table">
<tbody style="font-size: 15px;">
<tr>
<td>
<svg height="26" width="26">
<g class="nodes">
<circle cx="13" cy="13" r="13" fill="orange"></circle>
<text x="13" y="13" text-anchor="middle" dominant-baseline="central" class="{{ meta["svg_icon"]["style"] }}" font-size="16px">{{ meta["svg_icon"]["icon"] }}</text>
</g>
</svg>
{{ meta['type'] }}
</td>
<td><b>First Seen:</b> {% if meta['first_seen'] %}{{ meta['first_seen'][0:4] }}-{{ meta['first_seen'][4:6] }}-{{ meta['first_seen'][6:8] }}{% endif %}</td>
<td><b>Last Seen:</b> {% if meta['last_seen'] %}{{ meta['last_seen'][0:4] }}-{{ meta['last_seen'][4:6] }}-{{ meta['last_seen'][6:8] }}{% endif %}</td>
</tr>
</tbody>
</table>
</li>
<li class="list-group-item py-0">
<div id="accordion_image" class="my-3">
<div class="card">
<div class="card-header py-1" id="headingImage">
<button class="btn w-100 collapsed rotate" data-toggle="collapse" data-target="#collapseImage" aria-expanded="false" aria-controls="collapseImage">
<span class="row text-left">
<div class="col-11">
<span class="mt-2">
<i class="far fa-image"></i> Show Image&nbsp;&nbsp;
</span>
</div>
<div class="col-1 text-primary">
<i class="fas fa-chevron-circle-down"></i>
</div>
</span>
</button>
</div>
<div id="collapseImage" class="collapse show" aria-labelledby="headingImage" data-parent="#accordion_image">
<div class="card-body text-center">
{% include 'objects/image/block_blur_img_slider.html' %}
<img class="object_image mb-1" usemap="#image-map" src="{{ url_for('objects_ocr.ocr_image', filename=meta['id'])}}">
<map name="image-map">
{% for c in meta['map'] %}
<area shape="poly" coords="{{ c[0] }}" title="{{ c[1] }}">
{% endfor %}
</map>
</div>
</div>
</div>
</div>
</li>
<li class="list-group-item py-0">
<pre class="my-0" style="white-space: pre-wrap;">{{ meta['content'] }}</pre>
{% if meta['translation'] %}
<hr class="m-1">
<pre class="my-0 text-secondary" style="white-space: pre-wrap;">{{ meta['translation'] }}</pre>
{% endif %}
{% if not is_correlation %}
<div class="my-1">
{% set mess_id_escape= meta['id'] | replace("/", "_") %}
<span class="btn btn-outline-dark p-0 px-1" type="button" data-toggle="collapse" data-target="#collapseTrans{{ mess_id_escape }}" aria-expanded="false" aria-controls="collapseTrans{{ mess_id_escape }}">
<i class="fas fa-language"></i> {% if meta['language'] %}{{ meta['language'] }}{% endif %}
</span>
<div class="collapse" id="collapseTrans{{ mess_id_escape }}">
<div class="card card-body">
<form method="post" action="{{ url_for('languages_ui.translate_object') }}">
<input type="text" id="type" name="type" value="{{meta['type']}}" hidden>
<input type="text" id="id" name="id" value="{{meta['id']}}" hidden>
<span class="badge badge-primary">Source:</span>
<span class="">
<select id="language_target" name="language_target" class="form-select" aria-label="Message Language" onchange="$('#translation').val('');">
<option selected value="{{ meta['language'] }}">{{ meta['language'] }}</option>
{% for language in translation_languages %}
<option value="{{ language }}">{{ translation_languages[language] }}</option>
{% endfor %}
</select>
</span>
{% if translation_target %}
<input type="text" id="target" name="target" value="{{translation_target}}" hidden>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="badge badge-primary">Target:</span><span>{{translation_target}}</span>
<textarea class="form-control" id="translation" name="translation">{{ meta['translation'] }}</textarea>
<button class="btn btn-dark" type="submit">
<i class="fas fa-pen-alt"> Update Language or Translation</i>
</button>
{% else %}
<button class="btn btn-dark" type="submit">
<i class="fas fa-pen-alt"> Update Language</i>
</button>
{% endif %}
</form>
<div>
<a class="btn btn-primary" href="{{ url_for('languages_ui.detect_object_language')}}?type={{ meta['type'] }}&id={{ meta['id'] }}">
<i class="fas fa-redo"></i> Detect Language
</a>
</div>
</div>
</div>
</div>
{% endif %}
</li>
<li class="list-group-item py-0">
<div class="my-2">
Tags:
{% for tag in meta['tags'] %}
<button class="btn btn-{{ bootstrap_label[loop.index0 % 5] }}"
data-toggle="modal" data-target="#edit_tags_modal"
data-tagid="{{ tag }}" data-objtype="{{ meta['type'] }}" data-objsubtype="" data-objid="{{ meta["id"] }}">
{{ tag }}
</button>
{% endfor %}
<button type="button" class="btn btn-light" data-toggle="modal" data-target="#add_tags_modal">
<i class="far fa-plus-square"></i>
</button>
</div>
</li>
</ul>
{% with obj_type='ocr', obj_id=meta['id'], obj_subtype='' %}
{% include 'modals/investigations_register_obj.html' %}
{% endwith %}
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#investigations_register_obj_modal">
<i class="fas fa-microscope"></i> Investigations
</button>
<span class="mb-2 float-right">
{% if is_correlation %}
<a href="{{ url_for('objects_ocr.object_ocr')}}?subtype={{ meta['subtype'] }}&id={{ meta['id'] }}">
<button class="btn btn-info"><i class="fas fa-expand"></i> Show Object</button>
</a>
{% else %}
<a href="{{ url_for('correlation.show_correlation')}}?type={{ meta['type'] }}&subtype={{ meta['subtype'] }}&id={{ meta['id'] }}">
<button class="btn btn-info"><i class="far fa-eye"></i> Correlations &nbsp;
</button>
</a>
{% endif %}
</span>
</div>
</div>

View file

@ -0,0 +1,607 @@
<!DOCTYPE html>
<html>
<head>
<title>Qrcodes - AIL</title>
<link rel="icon" href="{{ url_for('static', filename='image/ail-icon.png') }}">
<!-- Core CSS -->
<link href="{{ url_for('static', filename='css/bootstrap4.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/dataTables.bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/daterangepicker.min.css') }}" rel="stylesheet">
<!-- JS -->
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/bootstrap4.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/moment.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery.daterangepicker.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/d3.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/d3/sparklines.js')}}"></script>
<style>
.input-group .form-control {
position: unset;
}
.line {
fill: none;
stroke: #000;
stroke-width: 2.0px;
}
.bar {
fill: steelblue;
}
.bar:hover{
fill: brown;
cursor: pointer;
}
.bar_stack:hover{
cursor: pointer;
}
.pie_path:hover{
cursor: pointer;
}
.svgText {
pointer-events: none;
}
div.tooltip {
position: absolute;
text-align: center;
padding: 2px;
font: 12px sans-serif;
background: #ebf4fb;
border: 2px solid #b7ddf2;
border-radius: 8px;
pointer-events: none;
color: #000000;
}
</style>
</head>
<body>
{% include 'nav_bar.html' %}
<div class="container-fluid">
<div class="row">
{% include 'sidebars/sidebar_objects.html' %}
<div class="col-12 col-lg-10" id="core_content">
<div class="row">
<div class="col-xl-10">
<div class="mt-1" id="barchart_type"></div>
{# {% include 'image/block_images_search.html' %}#}
</div>
<div class="col-xl-2">
<div class="card mb-3 mt-2" style="background-color:#d9edf7;">
<div class="card-body text-center py-2">
<h6 class="card-title" style="color:#286090;">Select a date range :</h6>
<form action="{{ url_for('objects_qrcode.objects_qrcodes_post') }}" id="hash_selector_form" method='post'>
<div class="input-group" id="date-range-from">
<div class="input-group-prepend"><span class="input-group-text"><i class="far fa-calendar-alt" aria-hidden="true"></i></span></div>
<input class="form-control" id="date-range-from-input" placeholder="yyyy-mm-dd" value="{{ date_from }}" name="date_from" autocomplete="off">
</div>
<div class="input-group" id="date-range-to">
<div class="input-group-prepend"><span class="input-group-text"><i class="far fa-calendar-alt" aria-hidden="true"></i></span></div>
<input class="form-control" id="date-range-to-input" placeholder="yyyy-mm-dd" value="{{ date_to }}" name="date_to" autocomplete="off">
</div>
<div class="form-check my-1">
<input class="form-check-input" type="checkbox" id="checkbox-input-show" name="show_objects" value="True" {% if show_objects %}checked{% endif %}>
<label class="form-check-label" for="checkbox-input-show">
<span style="color:#286090; font-size: 14px;">
Show Qrcodes <i class="fas fa-key"></i>
</span>
</label>
</div>
<button class="btn btn-primary" style="text-align:center;">
<i class="fas fa-copy"></i> Search
</button>
</form>
</div>
</div>
<div id="pie_chart_encoded">
</div>
<div id="pie_chart_top5_types">
</div>
</div>
</div>
{% if dict_objects %}
{% if date_from|string == date_to|string %}
<h3> {{ date_from }} Qrcodes Name: </h3>
{% else %}
<h3> {{ date_from }} to {{ date_to }} Qrcodes Name: </h3>
{% endif %}
<table id="tableb64" class="table table-striped table-bordered">
<thead class="bg-dark text-white">
<tr>
<th></th>
<th>First Seen</th>
<th>Last Seen</th>
<th>Total</th>
<th>Last days</th>
</tr>
</thead>
<tbody style="font-size: 15px;">
{% for obj_id in dict_objects %}
<tr>
<td style="word-break: break-all;">
<a target="_blank" href="{{ url_for('correlation.show_correlation') }}?type=qrcode&id={{ obj_id }}">{{ dict_objects[obj_id]['id'] }}</a>
{% if dict_objects[obj_id]['content'] %}
<div><small>{{ dict_objects[obj_id]['content'] }}</small></div>
{% endif %}
</td>
<td>{{ dict_objects[obj_id]['first_seen'] }}</td>
<td>{{ dict_objects[obj_id]['last_seen'] }}</td>
<td>{{ dict_objects[obj_id]['nb_seen'] }}</td>
<td id="sparklines_{{ obj_id }}" style="text-align:center;"></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
{% if show_objects %}
{% if date_from|string == date_to|string %}
<h3> {{ date_from }}, No Qrcode</h3>
{% else %}
<h3> {{ date_from }} to {{ date_to }}, No Qrcode</h3>
{% endif %}
{% endif %}
{% endif %}
</div>
</div>
</div>
<script>
var chart = {};
$(document).ready(function(){
$("#page-Decoded").addClass("active");
$("#nav_qrcode").addClass("active");
$('#date-range-from').dateRangePicker({
separator : ' to ',
getValue: function()
{
if ($('#date-range-from-input').val() && $('#date-range-to').val() )
return $('#date-range-from-input').val() + ' to ' + $('#date-range-to').val();
else
return '';
},
setValue: function(s,s1,s2)
{
$('#date-range-from-input').val(s1);
$('#date-range-to-input').val(s2);
},
});
$('#date-range-to').dateRangePicker({
separator : ' to ',
getValue: function()
{
if ($('#date-range-from-input').val() && $('#date-range-to').val() )
return $('#date-range-from-input').val() + ' to ' + $('#date-range-to').val();
else
return '';
},
setValue: function(s,s1,s2)
{
$('#date-range-from-input').val(s1);
$('#date-range-to-input').val(s2);
},
});
$('#date-range-from').data('dateRangePicker').setDateRange('{{date_from}}','{{date_to}}');
$('#date-range-to').data('dateRangePicker').setDateRange('{{date_from}}','{{date_to}}');
$('#tableb64').DataTable({
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],
"iDisplayLength": 10,
"order": [[ 3, "desc" ]]
});
chart.stackBarChart = barchart_type_stack("{{ url_for('objects_qrcode.objects_qrcodes_range_json') }}?date_from={{date_from}}&date_to={{date_to}}", 'id');
chart.onResize();
$(window).on("resize", function() {
chart.onResize();
});
});
function toggle_sidebar(){
if($('#nav_menu').is(':visible')){
$('#nav_menu').hide();
$('#side_menu').removeClass('border-right')
$('#side_menu').removeClass('col-lg-2')
$('#core_content').removeClass('col-lg-10')
}else{
$('#nav_menu').show();
$('#side_menu').addClass('border-right')
$('#side_menu').addClass('col-lg-2')
$('#core_content').addClass('col-lg-10')
}
}
</script>
<script>
{% for obj_id in dict_objects %}
sparkline("sparklines_{{ obj_id }}", {{ dict_objects[obj_id]['sparkline'] }}, {});
{% endfor %}
</script>
<script>
var margin = {top: 20, right: 100, bottom: 55, left: 45},
width = 1000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1);
var y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var color = d3.scaleOrdinal(d3.schemeSet3);
var svg = d3.select("#barchart_type").append("svg")
.attr("id", "thesvg")
.attr("viewBox", "0 0 1000 500")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
function barchart_type_stack(url, id) {
d3.json(url)
.then(function(data){
var labelVar = 'date'; //A
var varNames = d3.keys(data[0])
.filter(function (key) { return key !== labelVar;}); //B
data.forEach(function (d) { //D
var y0 = 0;
d.mapping = varNames.map(function (name) {
return {
name: name,
label: d[labelVar],
y0: y0,
y1: y0 += +d[name]
};
});
d.total = d.mapping[d.mapping.length - 1].y1;
});
x.domain(data.map(function (d) { return (d.date); })); //E
y.domain([0, d3.max(data, function (d) { return d.total; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("class", "bar")
{% if date_from|string == date_to|string and type is none %}
.on("click", function (d) { window.location.href = "{{ url_for('objects_qrcode.objects_qrcodes') }}?date_from={{date_from}}&date_to={{date_to}}&type_id="+d })
.attr("transform", "rotate(-18)" )
{% elif date_from|string == date_to|string and type is not none %}
.on("click", function (d) { window.location.href = "{{ url_for('objects_qrcode.objects_qrcodes') }}?date_from="+d+'&date_to='+d })
.attr("transform", "rotate(-18)" )
{% else %}
.on("click", function (d) { window.location.href = "{{ url_for('objects_qrcode.objects_qrcodes') }}?date_from="+d+'&date_to='+d })
.attr("transform", "rotate(-40)" )
{% endif %}
.style("text-anchor", "end");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
var selection = svg.selectAll(".series")
.data(data)
.enter().append("g")
.attr("class", "series")
.attr("transform", function (d) { return "translate(" + x((d.date)) + ",0)"; });
selection.selectAll("rect")
.data(function (d) { return d.mapping; })
.enter().append("rect")
.attr("class", "bar_stack")
.attr("width", x.bandwidth())
.attr("y", function (d) { return y(d.y1); })
.attr("height", function (d) { return y(d.y0) - y(d.y1); })
.style("fill", function (d) { return color(d.name); })
.style("stroke", "grey")
.on("mouseover", function (d) { showPopover.call(this, d); })
.on("mouseout", function (d) { removePopovers(); })
{% if date_from|string == date_to|string and type is none %}
.on("click", function(d){ window.location.href = "{{ url_for('objects_qrcode.objects_qrcodes') }}" +'?date_from={{date_from}}&date_to={{date_to}}&type_id='+d.label+'&encoding='+d.name; });
{% elif date_from|string == date_to|string and type is not none %}
.on("click", function(d){ window.location.href = "{{ url_for('objects_qrcode.objects_qrcodes') }}" +'?type_id={{type_id}}&date_from='+d.label+'&date_to='+d.label+'&encoding='+d.name; });
{% else %}
.on("click", function(d){ window.location.href = "{{ url_for('objects_qrcode.objects_qrcodes') }}" +'?type_id='+ d.name +'&date_from='+d.label+'&date_to='+d.label; });
{% endif %}
data.forEach(function(d) {
if(d.total !== 0){
svg.append("text")
.attr("class", "bar")
.attr("dy", "-.35em")
.attr('x', x(d.date) + x.bandwidth()/2)
.attr('y', y(d.total))
{% if date_from|string == date_to|string and type is none %}
.on("click", function () {window.location.href = "{{ url_for('objects_qrcode.objects_qrcodes') }}"+'?date_from={{date_from}}&date_to={{date_to}}&type_id='+d.date })
{% elif date_from|string == date_to|string and type is not none %}
.on("click", function () {window.location.href = "{{ url_for('objects_qrcode.objects_qrcodes') }}?type_id={{type_id}}&date_from="+d.date+'&date_to='+d.date })
{% else %}
.on("click", function () {window.location.href = "{{ url_for('objects_qrcode.objects_qrcodes') }}"+'?date_from='+d.date+'&date_to='+d.date })
{% endif %}
.style("text-anchor", "middle")
.text(d.total);
}
});
drawLegend(varNames);
});
}
function drawLegend (varNames) {
var legend = svg.selectAll(".legend")
.data(varNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", 943)
.attr("width", 10)
.attr("height", 10)
.style("fill", color)
.style("stroke", "grey");
legend.append("text")
.attr("class", "svgText")
.attr("x", 941)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function (d) { return d; });
}
function removePopovers () {
$('.popover').each(function() {
$(this).remove();
});
}
function showPopover (d) {
$(this).popover({
title: "<b><span id='tooltip-id-name-bar'></span></b>",
placement: 'top',
container: 'body',
trigger: 'manual',
html : true,
content: function() {
return "<span id='tooltip-id-label'></span>" +
"<br/>num: <span id='tooltip-id-value-bar'></span>"; }
});
$(this).popover('show');
$("#tooltip-id-name-bar").text(d.name);
$("#tooltip-id-label").text(d.label);
$("#tooltip-id-value-bar").text(d3.format(",")(d.value ? d.value: d.y1 - d.y0));
}
chart.onResize = function () {
var aspect = 1000 / 500, chart = $("#thesvg");
var targetWidth = chart.parent().width();
chart.attr("width", targetWidth);
chart.attr("height", targetWidth / aspect);
}
window.chart = chart;
</script>
<script>
function draw_pie_chart(id, url_json, pie_on_click_url) {
var width_pie = 200;
var height_pie = 200;
var padding_pie = 10;
var opacity_pie = .8;
var radius_pie = Math.min(width_pie - padding_pie, height_pie - padding_pie) / 2;
//var color_pie = d3.scaleOrdinal(d3.schemeCategory10);
var color_pie = d3.scaleOrdinal(d3.schemeSet3);
var div_pie = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var svg_pie = d3.select("#"+id)
.append('svg')
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox','0 0 '+Math.min(width_pie,height_pie) +' '+Math.min(width_pie,height_pie) )
.attr('preserveAspectRatio','xMinYMin')
var g_pie = svg_pie.append('g')
.attr('transform', 'translate(' + (width_pie/2) + ',' + (height_pie/2) + ')');
var arc_pie = d3.arc()
.innerRadius(0)
.outerRadius(radius_pie);
d3.json(url_json)
.then(function(data){
var pie_pie = d3.pie()
.value(function(d) { return d.value; })
.sort(null);
var path_pie = g_pie.selectAll('path')
.data(pie_pie(data))
.enter()
.append("g")
.append('path')
.attr('d', arc_pie)
.attr('fill', (d,i) => color_pie(i))
.attr('class', 'pie_path')
.on("mouseover", mouseovered_pie)
.on("mouseout", mouseouted_pie)
.on("click", function (d) {window.location.href = pie_on_click_url+d.data.name })
.style('opacity', opacity_pie)
.style('stroke', 'white');
});
function mouseovered_pie(d) {
//remove old content
$("#tooltip-id-name").remove();
$("#tooltip-id-value").remove();
// tooltip
var content;
content = "<b><span id='tooltip-id-name'></span></b><br/>"+
"<br/>"+
"<i>Decoded</i>: <span id='tooltip-id-value'></span><br/>"
div_pie.transition()
.duration(200)
.style("opacity", .9);
div_pie.html(content)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
$("#tooltip-id-name").text(d.data.name);
$("#tooltip-id-value").text(d.data.value);
}
function mouseouted_pie() {
div_pie.transition()
.duration(500)
.style("opacity", 0);
}
}
</script>
<script>
function barchart_type(url, id) {
var margin = {top: 20, right: 20, bottom: 70, left: 40};
var width = 960 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1);
var y = d3.scaleLinear().rangeRound([height, 0]);
var xAxis = d3.axisBottom(x)
//.tickFormat(d3.time.format("%Y-%m"));
var yAxis = d3.axisLeft(y)
.ticks(10);
/*var svg = d3.select(id).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id", "thesvg")
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");*/
d3.json(url)
.then(function(data){
data.forEach(function(d) {
d.value = +d.value;
});
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var label = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
{% if daily_type_chart %}
.attr("transform", "rotate(-20)" );
{% else %}
.attr("transform", "rotate(-70)" )
.attr("class", "bar")
{% endif %}
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Value ($)");
var bar = svg.selectAll("bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
//.style("fill", "steelblue")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
data.forEach(function(d) {
if(d.value != 0){
svg.append("text")
.attr("class", "bar")
.attr("dy", "-.35em")
//.text(function(d) { return d.value; });
.text(d.value)
.style("text-anchor", "middle")
.attr('x', x(d.date) + x.bandwidth()/2)
.attr('y', y(d.value));
}
});
});
}
</script>
</body>
</html>

View file

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html>
<head>
<title>Qrcode - AIL</title>
<link rel="icon" href="{{ url_for('static', filename='image/ail-icon.png') }}">
<!-- Core CSS -->
<link href="{{ url_for('static', filename='css/bootstrap4.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/dataTables.bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/tags.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ url_for('static', filename='css/ail-project.css') }}" rel="stylesheet">
<!-- JS -->
<script src="{{ url_for('static', filename='js/jquery.js')}}"></script>
<script src="{{ url_for('static', filename='js/popper.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/bootstrap4.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/tags.js') }}"></script>
</head>
<body>
{% include 'nav_bar.html' %}
<div class="container-fluid">
<div class="row">
{% include 'sidebars/sidebar_objects.html' %}
<div class="col-12 col-lg-10" id="core_content">
{% with meta=meta, is_correlation=False %}
{% include 'objects/qrcode/card_qrcode.html' %}
{% endwith %}
</div>
</div>
</div>
<script>
var chart = {};
$(document).ready(function(){
$("#page-Decoded").addClass("active");
$("#nav_chat").addClass("active");
});
function toggle_sidebar(){
if($('#nav_menu').is(':visible')){
$('#nav_menu').hide();
$('#side_menu').removeClass('border-right')
$('#side_menu').removeClass('col-lg-2')
$('#core_content').removeClass('col-lg-10')
}else{
$('#nav_menu').show();
$('#side_menu').addClass('border-right')
$('#side_menu').addClass('col-lg-2')
$('#core_content').addClass('col-lg-10')
}
}
</script>
</body>
</html>

View file

@ -37,38 +37,6 @@
</table> </table>
</li> </li>
<li class="list-group-item py-0"> <li class="list-group-item py-0">
<div id="accordion_image" class="my-3">
<div class="card">
<div class="card-header py-1" id="headingImage">
<button class="btn w-100 collapsed rotate" data-toggle="collapse" data-target="#collapseImage" aria-expanded="false" aria-controls="collapseImage">
<span class="row text-left">
<div class="col-11">
<span class="mt-2">
<i class="far fa-image"></i> Show Image&nbsp;&nbsp;
</span>
</div>
<div class="col-1 text-primary">
<i class="fas fa-chevron-circle-down"></i>
</div>
</span>
</button>
</div>
{# <div id="collapseImage" class="collapse show" aria-labelledby="headingImage" data-parent="#accordion_image">#}
{# <div class="card-body text-center">#}
{# {% include 'objects/image/block_blur_img_slider.html' %}#}
{##}
{# <img class="object_image mb-1" usemap="#image-map" src="{{ url_for('objects_ocr.ocr_image', filename=meta['id'])}}">#}
{# <map name="image-map">#}
{# {% for c in meta['map'] %}#}
{# <area shape="poly" coords="{{ c[0] }}" title="{{ c[1] }}">#}
{# {% endfor %}#}
{# </map>#}
{# </div>#}
{# </div>#}
</div>
</div>
</li>
<li class="list-group-item py-0">
</li> </li>

View file

@ -88,6 +88,12 @@
<span>OCR</span> <span>OCR</span>
</a> </a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="{{url_for('objects_qrcode.objects_qrcodes')}}" id="nav_qrcode">
<i class="fas fa-qrcode"></i>
<span>Qrcode</span>
</a>
</li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{url_for('objects_title.objects_titles')}}" id="nav_title"> <a class="nav-link" href="{{url_for('objects_title.objects_titles')}}" id="nav_title">
<i class="fas fa-heading"></i> <i class="fas fa-heading"></i>

View file

@ -228,10 +228,12 @@
{% endwith %} {% endwith %}
{% else %} {% else %}
{% if 'date' in dict_tagged and 'current_tags' in dict_tagged %}
<h2> <h2>
{% if dict_tagged['date']['date_from'] %}{{ dict_tagged['date']['date_from'][0:4] }}-{{ dict_tagged['date']['date_from'][4:6] }}-{{ dict_tagged['date']['date_from'][6:8] }} {% endif %} {% if dict_tagged['date']['date_from'] %}{{ dict_tagged['date']['date_from'][0:4] }}-{{ dict_tagged['date']['date_from'][4:6] }}-{{ dict_tagged['date']['date_from'][6:8] }} {% endif %}
- {{ dict_tagged['date']['date_to'][0:4] }}-{{ dict_tagged['date']['date_to'][4:6] }}-{{ dict_tagged['date']['date_to'][6:8] }} No Result - {{ dict_tagged['date']['date_to'][0:4] }}-{{ dict_tagged['date']['date_to'][4:6] }}-{{ dict_tagged['date']['date_to'][6:8] }} No Result
</h2> </h2>
{% endif %}
{% for tag in dict_tagged['current_tags'] %} {% for tag in dict_tagged['current_tags'] %}
<span class="badge badge-secondary my-1">{{ tag }}"</span> <span class="badge badge-secondary my-1">{{ tag }}"</span>
{% endfor %} {% endfor %}