diff --git a/scripts/ilap_artifacts.py b/scripts/ilap_artifacts.py
index 7c01bf46..c5b95fd0 100644
--- a/scripts/ilap_artifacts.py
+++ b/scripts/ilap_artifacts.py
@@ -207,7 +207,7 @@
'notificationsXI': ('Notifications', '*PushStore*'),
'notificationsXII': ('Notifications', '*private/var/mobile/Library/UserNotifications*'),
'ooklaSpeedtestData': ('Applications', '**/speedtest.sqlite*'),
- 'photosMetadata': ('Photos', '**/Photos.sqlite'),
+ #'photosMetadata': ('Photos', '**/Photos.sqlite'),
'powerlogAll': ('Powerlog', '**/CurrentPowerlog.PLSQL'),
'powerlogGZ': ('Powerlog Backups', '**/Library/BatteryLife/Archives/powerlog_*.PLSQL.gz'),
'queryPredictions': ('SMS & iMessage', '**/query_predictions.db'),
diff --git a/scripts/location_map_constants.py b/scripts/location_map_constants.py
new file mode 100644
index 00000000..136d58b5
--- /dev/null
+++ b/scripts/location_map_constants.py
@@ -0,0 +1,106 @@
+# coding: utf-8
+iLEAPP_KMLs = "_KML Exports"
+
+defaultShadowUrl = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png'
+
+defaultIconUrl = 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-{}.png'
+
+colors = ['red','green','blue','orange','violet','yellow', 'sienna', 'indigo','purple','cyan','darkgray']
+
+legend_tag = "
{1}"
+
+legend_title_tag = "{0}
"
+
+legend_div = """
+
+"""
+
+template_part1 = """
+{% macro html(this, kwargs) %}
+
+
+
+
+
+
+
+ jQuery UI Draggable - Default functionality
+
+
+
+
+
+
+
+
+
+
+
+"""
+template_part2 = """
+
+
+
+
+
+
+{% endmacro %}
+"""
+
diff --git a/scripts/location_map_report.py b/scripts/location_map_report.py
new file mode 100644
index 00000000..20ed1b39
--- /dev/null
+++ b/scripts/location_map_report.py
@@ -0,0 +1,126 @@
+# coding: utf-8
+#Import the necessary Python modules
+import pandas as pd
+import folium
+from folium.plugins import TimestampedGeoJson
+from shapely.geometry import Point
+import os
+from datetime import datetime
+from branca.element import Template, MacroElement
+import html
+from scripts.location_map_constants import iLEAPP_KMLs, defaultShadowUrl, defaultIconUrl, colors, legend_tag, legend_title_tag, legend_div, template_part1, template_part2
+import sqlite3
+
+from scripts.artifact_report import ArtifactHtmlReport
+
+#Helpers
+def htmlencode(string):
+ return string.encode(encoding='ascii',errors='xmlcharrefreplace').decode('utf-8')
+
+def geodfToFeatures(df, f, props):
+ coords = []
+ times = []
+ for i,row in df[df.Description.str.contains(f)].iterrows():
+ coords.append(
+ [row.Point.x,row.Point.y]
+ )
+ times.append(datetime.strptime(row.Name,'%Y-%m-%d %H:%M:%S').isoformat())
+ return {
+ 'type': 'Feature',
+ 'geometry': {
+ 'type': props[f]['fType'],
+ 'coordinates': coords,
+ },
+ 'properties': {
+ 'times': times,
+ 'style': {'color': props[f]['color']},
+ 'icon': props[f]['icon'],
+ 'iconstyle': {
+ 'iconUrl': props[f]['iconUrl'],
+ 'shadowUrl': props[f]['shadowUrl'],
+ 'iconSize': [25, 41],
+ 'iconAnchor': [12, 41],
+ 'popupAnchor': [1, -34],
+ 'shadowSize': [41, 41],
+ 'radius': 5,
+ },
+ },
+ }
+
+
+def generate_location_map(reportfolderbase,legend_title):
+ KML_path = os.path.join(reportfolderbase,iLEAPP_KMLs)
+ if not os.path.isdir(KML_path) or not os.listdir(KML_path):
+ return
+
+ location_path = os.path.join(reportfolderbase, 'LOCATIONS')
+ os.makedirs(location_path,exist_ok=True)
+ db = sqlite3.connect(os.path.join(KML_path,"_latlong.db"))
+ df = pd.read_sql_query("SELECT key as Name, Activity as Description, latitude, longitude FROM data ;", db)
+ df["Point"] = df.apply(lambda row: Point(float(row['longitude']),float(row['latitude']),.0), axis=1)
+
+ #sorting is needed for correct display
+ df.sort_values(by=['Name'],inplace=True)
+
+ #Parse geo data and add to Folium Map
+ data_names = df[~df.Description.str.contains('Photos')].Description.unique()
+ featuresProp = {}
+
+ for c,d in zip(colors, data_names):
+ descFilter = d
+ if 'ZRT' in d:
+ fType = 'LineString'
+ icon = 'marker'
+ iconUrl = defaultIconUrl.format(c)
+ shadowUrl = defaultShadowUrl
+ else:
+ fType = 'MultiPoint'
+ icon = 'circle'
+ iconUrl = ''
+ shadowUrl = ''
+
+ color = c
+
+ featuresProp[d] = {
+ 'fType': fType,
+ 'color': c,
+ 'icon': icon,
+ 'iconUrl': iconUrl,
+ 'shadowUrl': defaultShadowUrl,
+ }
+
+ location_map = folium.Map([df.iloc[0].Point.y,df.iloc[0].Point.x], prefer_canvas=True, zoom_start = 6)
+ bounds = (df[~df.Description.str.contains('Photos')]['longitude'].min(),
+ df[~df.Description.str.contains('Photos')]['latitude'].min(),
+ df[~df.Description.str.contains('Photos')]['longitude'].max(),
+ df[~df.Description.str.contains('Photos')]['latitude'].max(),
+ )
+ location_map.fit_bounds([
+ (bounds[1],bounds[0]),
+ (bounds[3],bounds[2]),
+ ]
+ )
+
+ tsGeo = TimestampedGeoJson({
+ 'type': 'FeatureCollection',
+ 'features': [
+ geodfToFeatures(df, f, featuresProp) for f in data_names
+ ]
+ }, period="PT1M", duration="PT1H", loop=False, transition_time = 50, time_slider_drag_update=True, add_last_point=True, max_speed=200).add_to(location_map)
+
+
+ #legend
+ legend = '\n'.join([ legend_tag.format(featuresProp[f]['color'], htmlencode(f)) for f in data_names])
+ template = '\n'.join([template_part1, legend_title_tag.format(htmlencode(legend_title)), legend_div.format(legend), template_part2])
+
+ macro = MacroElement()
+ macro._template = Template(template)
+
+ location_map.get_root().add_child(macro)
+
+ location_map.save(os.path.join(location_path,"Locations_Map.html"))
+
+ report = ArtifactHtmlReport('Locations Map')
+ report.start_artifact_report(location_path, 'Locations Map', 'Map plotting all locations')
+ report.write_raw_html(open(os.path.join(location_path,"Locations_Map.html")).read())
+ report.end_artifact_report()
diff --git a/scripts/report.py b/scripts/report.py
index 33663e14..ce82dfda 100644
--- a/scripts/report.py
+++ b/scripts/report.py
@@ -9,6 +9,7 @@
from scripts.html_parts import *
from scripts.ilapfuncs import logfunc
from scripts.version_info import aleapp_version, aleapp_contributors
+from scripts.location_map_report import generate_location_map
def get_icon_name(category, artifact):
@@ -227,6 +228,8 @@ def get_icon_name(category, artifact):
icon = 'user'
return icon
+ #generate loaation map with all KML files
+ generate_location_map(reportfolderbase, "Legend")
def generate_report(reportfolderbase, time_in_secs, time_HMS, extraction_type, image_input_path):
control = None
diff --git a/scripts/version_info.py b/scripts/version_info.py
index 5877c63f..78f1b2de 100644
--- a/scripts/version_info.py
+++ b/scripts/version_info.py
@@ -4,31 +4,31 @@
# Format = [ Name, Blog-url, Twitter-handle, Github-url]
# Leave blank if not available
aleapp_contributors = [
- ['Alexis Brignoni', 'https://abrignoni.com', '@AlexisBrignoni', 'https://github.com/abrignoni'],
- ['Yogesh Khatri', 'https://swiftforensics.com', '@SwiftForensics', 'https://github.com/ydkhatri'],
- ['Agam Dua', 'https://loopback.dev', '@loopbackdev', 'https://github.com/agamdua'],
- ['Sarah Edwards', 'https://www.mac4n6.com', '@iamevltwin', 'https://github.com/mac4n6'],
- ['Heather Mahalik', 'https://smarterforensics.com', '@HeatherMahalik', ''],
- ['Jessica Hyde', 'https://twitter.com/B1N2H3X', '@B1N2H3X', ''],
- ['Phill Moore', 'https://thinkdfir.com', '@phillmoore', 'https://github.com/randomaccess3'],
- ['Mattia Epifani', 'https://blog.digital-forensics.it', '@mattiaep', ''],
- ['Mike Williamson', 'https://forensicmike1.com', '@forensicmike1', 'https://github.com/forensicmike'],
- ['Geraldine Blay', 'https://gforce4n6.blogspot.com', '@i_am_the_gia', ''],
- ['Christopher Vance', 'https://blog.d204n6.com', '@cScottVance', ''],
- ['Brooke Gottlieb', '', '@xbrookego', ''],
- ['Jack Farley', 'http://farleyforensics.com', '@JackFarley248', ''],
- ['Shafik Punja', '', '@qubytelogic', ''],
- ['Cheeky4N6Monkey', 'https://cheeky4n6monkey.blogspot.com', '@Cheeky4n6Monkey', 'https://github.com/cheeky4n6monkey'],
- ['Edward Greybeard', '', '', 'https://github.com/edward-greybeard'],
- ['Douglas Kein', '', '@DouglasKein', ''],
- ['Claudia Meda', '', '@KlodiaMaida', 'https://github.com/KlodiaMaida'],
- ['Silvia Spallarossa', '', '@SilviaSpallaro1', 'https://github.com/spatimbs'],
- ['Francesca Maestri', '', '@franc3sca_m', 'https://github.com/francyM'],
- ['Christopher Vance', 'https://blog.d204n6.com/', '@cScottVance', 'https://github.com/cScottVance'],
- ['Jesse Spangenberger', 'https://cyberfenixtech.blogspot.com/', '@AzuleOnyx', 'https://github.com/flamusdiu'],
- ['Jon Baumann', 'https://ciofecaforensics.com/', '@CiofecaForensic', 'https://github.com/threeplanetssoftware'],
- ['Scott Koenig', '', '@Scott_Kjr', ''],
- ['Kevin Pagano', 'https://stark4n6.com/', '@KevinPagano3', 'https://github.com/stark4n6'],
+ [ 'Alexis Brignoni', 'https://abrignoni.com', '@AlexisBrignoni', 'https://github.com/abrignoni'],
+ [ 'Yogesh Khatri', 'https://swiftforensics.com', '@SwiftForensics', 'https://github.com/ydkhatri'],
+ [ 'Agam Dua', 'https://loopback.dev', '@loopbackdev', 'https://github.com/agamdua'],
+ [ 'Sarah Edwards', 'https://www.mac4n6.com', '@iamevltwin', 'https://github.com/mac4n6'],
+ [ 'Heather Mahalik', 'https://smarterforensics.com', '@HeatherMahalik', ''],
+ [ 'Jessica Hyde', 'https://twitter.com/B1N2H3X', '@B1N2H3X', ''],
+ [ 'Phill Moore', 'https://thinkdfir.com', '@phillmoore', 'https://github.com/randomaccess3'],
+ [ 'Mattia Epifani', 'https://blog.digital-forensics.it', '@mattiaep', ''],
+ [ 'Mike Williamson', 'https://forensicmike1.com', '@forensicmike1', 'https://github.com/forensicmike'],
+ [ 'Geraldine Blay', 'https://gforce4n6.blogspot.com', '@i_am_the_gia', ''],
+ [ 'Christopher Vance', 'https://blog.d204n6.com', '@cScottVance', ''],
+ [ 'Brooke Gottlieb', '', '@xbrookego', ''],
+ [ 'Jack Farley', 'http://farleyforensics.com', '@JackFarley248', ''],
+ [ 'Shafik Punja', '', '@qubytelogic', ''],
+ [ 'Cheeky4N6Monkey', 'https://cheeky4n6monkey.blogspot.com', '@Cheeky4n6Monkey', 'https://github.com/cheeky4n6monkey'],
+ [ 'Edward Greybeard', '', '', 'https://github.com/edward-greybeard'],
+ [ 'Douglas Kein', '', '@DouglasKein', ''],
+ [ 'Claudia Meda', '', '@KlodiaMaida', 'https://github.com/KlodiaMaida'],
+ [ 'Silvia Spallarossa', '', '@SilviaSpallaro1', 'https://github.com/spatimbs'],
+ [ 'Francesca Maestri', '', '@franc3sca_m', 'https://github.com/francyM'],
+ [ 'Christopher Vance', 'https://blog.d204n6.com/', '@cScottVance', 'https://github.com/cScottVance'],
+ [ 'Jesse Spangenberger', 'https://cyberfenixtech.blogspot.com/', '@AzuleOnyx', 'https://github.com/flamusdiu'],
+ [ 'Jon Baumann', 'https://ciofecaforensics.com/', '@CiofecaForensic', 'https://github.com/threeplanetssoftware'],
+ [ 'Scott Koenig', '', '@Scott_Kjr', ''],
+ [ 'Kevin Pagano', 'https://stark4n6.com/', '@KevinPagano3', 'https://github.com/stark4n6'],
['Ed Michael', '', '@EdXlg123', 'https://github.com/edmichael'],
['Anna-Mariya Mateyna', '', '@any333_snickers', 'https://github.com/any333'],
['Tommy Harris', '', '@tobraha', 'https://github.com/tobraha']