"""SQLite parser plugin for Safari history database files."""
from dfdatetime import cocoa_time as dfdatetime_cocoa_time
from plaso.containers import events
from plaso.parsers import sqlite
from plaso.parsers.sqlite_plugins import interface
[docs]
class SafariHistoryPageVisitedEventData(events.EventData):
"""Safari history event data.
Attributes:
host (str): hostname of the server.
last_visited_time (dfdatetime.DateTimeValues): date and time the URL was
last visited.
offset (str): identifier of the row, from which the event data was
extracted.
query (str): SQL query that was used to obtain the event data.
title (str): title of the webpage visited.
url (str): URL visited.
visit_count (int): number of times the website was visited.
was_http_non_get (bool): True if the webpage was visited using a
non-GET HTTP request.
"""
DATA_TYPE = "safari:history:visit_sqlite"
[docs]
def __init__(self):
"""Initializes event data."""
super().__init__(data_type=self.DATA_TYPE)
self.host = None
self.last_visited_time = None
self.offset = None
self.query = None
self.title = None
self.url = None
self.visit_count = None
self.visit_redirect_source = None
self.was_http_non_get = None
[docs]
class SafariHistoryPluginSqlite(interface.SQLitePlugin):
"""SQLite parser plugin for Safari history database files.
The Safari history database file is typically stored in:
History.db
"""
NAME = "safari_historydb"
DATA_FORMAT = "Safari history SQLite database (History.db) file"
REQUIRED_STRUCTURE = {
"history_items": frozenset(["id", "url", "visit_count"]),
"history_visits": frozenset(
[
"id",
"history_item",
"visit_time",
"redirect_destination",
"title",
"http_non_get",
"redirect_source",
]
),
}
QUERIES = [
(
(
"SELECT history_items.id, history_items.url, history_items.visit"
"_count, history_visits.id AS visit_id, history_visits.history_item,"
"history_visits.visit_time, history_visits.redirect_destination, "
"history_visits.title, history_visits.http_non_get, "
"history_visits.redirect_source "
"FROM history_items, history_visits "
"WHERE history_items.id = history_visits.history_item"
),
"_ParsePageVisitRow",
)
]
SCHEMAS = [
{
"history_client_versions": (
"CREATE TABLE history_client_versions (client_version INTEGER "
"PRIMARY KEY,last_seen REAL NOT NULL)"
),
"history_event_listeners": (
"CREATE TABLE history_event_listeners (listener_name TEXT PRIMARY "
"KEY NOT NULL UNIQUE,last_seen REAL NOT NULL)"
),
"history_events": (
"CREATE TABLE history_events (id INTEGER PRIMARY KEY "
"AUTOINCREMENT,event_type TEXT NOT NULL,event_time REAL NOT "
"NULL,pending_listeners TEXT NOT NULL,value BLOB)"
),
"history_items": (
"CREATE TABLE history_items (id INTEGER PRIMARY KEY "
"AUTOINCREMENT,url TEXT NOT NULL UNIQUE,domain_expansion TEXT "
"NULL,visit_count INTEGER NOT NULL,daily_visit_counts BLOB NOT "
"NULL,weekly_visit_counts BLOB NULL,autocomplete_triggers BLOB "
"NULL,should_recompute_derived_visit_counts INTEGER NOT "
"NULL,visit_count_score INTEGER NOT NULL)"
),
"history_tombstones": (
"CREATE TABLE history_tombstones (id INTEGER PRIMARY KEY "
"AUTOINCREMENT,start_time REAL NOT NULL,end_time REAL NOT NULL,url "
"TEXT,generation INTEGER NOT NULL DEFAULT 0)"
),
"history_visits": (
"CREATE TABLE history_visits (id INTEGER PRIMARY KEY "
"AUTOINCREMENT,history_item INTEGER NOT NULL REFERENCES "
"history_items(id) ON DELETE CASCADE,visit_time REAL NOT NULL,title "
"TEXT NULL,load_successful BOOLEAN NOT NULL DEFAULT 1,http_non_get "
"BOOLEAN NOT NULL DEFAULT 0,synthesized BOOLEAN NOT NULL DEFAULT "
"0,redirect_source INTEGER NULL UNIQUE REFERENCES "
"history_visits(id) ON DELETE CASCADE,redirect_destination INTEGER "
"NULL UNIQUE REFERENCES history_visits(id) ON DELETE CASCADE,origin "
"INTEGER NOT NULL DEFAULT 0,generation INTEGER NOT NULL DEFAULT "
"0,attributes INTEGER NOT NULL DEFAULT 0,score INTEGER NOT NULL "
"DEFAULT 0)"
),
"metadata": ("CREATE TABLE metadata (key TEXT NOT NULL UNIQUE, value)"),
}
]
def _GetDateTimeRowValue(self, query_hash, row, value_name):
"""Retrieves a date and time value from the row.
Args:
query_hash (int): hash of the query, that uniquely identifies the query
that produced the row.
row (sqlite3.Row): row.
value_name (str): name of the value.
Returns:
dfdatetime.CocoaTime: date and time value or None if not available.
"""
timestamp = self._GetRowValue(query_hash, row, value_name)
if timestamp is None:
return None
return dfdatetime_cocoa_time.CocoaTime(timestamp=timestamp)
def _ParsePageVisitRow(self, parser_mediator, query, row, **unused_kwargs):
"""Parses a visited row.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
query (str): query that created the row.
row (sqlite3.Row): row.
"""
query_hash = hash(query)
was_http_non_get = self._GetRowValue(query_hash, row, "http_non_get")
event_data = SafariHistoryPageVisitedEventData()
event_data.last_visited_time = self._GetDateTimeRowValue(
query_hash, row, "visit_time"
)
event_data.offset = self._GetRowValue(query_hash, row, "id")
event_data.query = query
event_data.title = self._GetRowValue(query_hash, row, "title") or None
event_data.url = self._GetRowValue(query_hash, row, "url")
event_data.visit_count = self._GetRowValue(query_hash, row, "visit_count")
event_data.was_http_non_get = bool(was_http_non_get)
parser_mediator.ProduceEventData(event_data)
sqlite.SQLiteParser.RegisterPlugin(SafariHistoryPluginSqlite)