"""SQLite parser plugin for Mozilla Firefox history database files."""
from plaso.containers import events
from plaso.parsers import sqlite
from plaso.parsers.sqlite_plugins import interface
[docs]
class FirefoxPlacesBookmarkEventData(events.EventData):
"""Firefox bookmark event data.
Attributes:
added_time (dfdatetime.DateTimeValues): date and time the bookmark was
added.
bookmark_type (int): bookmark type.
host (str): visited hostname.
modification_time (dfdatetime.DateTimeValues): date and time the bookmark
was last modified.
offset (str): identifier of the row, from which the event data was
extracted.
places_title (str): places title.
query (str): SQL query that was used to obtain the event data.
title (str): title of the bookmark folder.
url (str): bookmarked URL.
visit_count (int): visit count.
"""
DATA_TYPE = "firefox:places:bookmark"
[docs]
def __init__(self):
"""Initializes event data."""
super().__init__(data_type=self.DATA_TYPE)
self.added_time = None
self.bookmark_type = None
self.host = None
self.modification_time = None
self.offset = None
self.places_title = None
self.query = None
self.title = None
self.url = None
self.visit_count = None
[docs]
class FirefoxPlacesBookmarkAnnotationEventData(events.EventData):
"""Firefox bookmark annotation event data.
Attributes:
added_time (dfdatetime.DateTimeValues): date and time the bookmark
annotation was added.
content (str): annotation content.
modification_time (dfdatetime.DateTimeValues): date and time the bookmark
annotation was last modified.
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 bookmark folder.
url (str): bookmarked URL.
"""
DATA_TYPE = "firefox:places:bookmark_annotation"
[docs]
def __init__(self):
"""Initializes event data."""
super().__init__(data_type=self.DATA_TYPE)
self.added_time = None
self.content = None
self.modification_time = None
self.offset = None
self.query = None
self.title = None
self.url = None
[docs]
class FirefoxPlacesBookmarkFolderEventData(events.EventData):
"""Firefox bookmark folder event data.
Attributes:
added_time (dfdatetime.DateTimeValues): date and time the bookmark folder
was added.
modification_time (dfdatetime.DateTimeValues): date and time the bookmark
folder was last modified.
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 bookmark folder.
"""
DATA_TYPE = "firefox:places:bookmark_folder"
[docs]
def __init__(self):
"""Initializes event data."""
super().__init__(data_type=self.DATA_TYPE)
self.added_time = None
self.modification_time = None
self.offset = None
self.query = None
self.title = None
[docs]
class FirefoxPlacesPageVisitedEventData(events.EventData):
"""Firefox page visited event data.
Attributes:
from_visit (str): URL that referred to the visited page.
hidden (str): value to indicated if the URL was hidden.
host (str): visited hostname.
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 visited page.
typed (str): value to indicated if the URL was typed.
url (str): URL of the visited page.
visit_count (int): visit count.
visit_type (str): transition type for the event.
"""
DATA_TYPE = "firefox:places:page_visited"
[docs]
def __init__(self):
"""Initializes event data."""
super().__init__(data_type=self.DATA_TYPE)
self.from_visit = None
self.hidden = None
self.host = None
self.last_visited_time = None
self.offset = None
self.query = None
self.title = None
self.typed = None
self.url = None
self.visit_count = None
self.visit_type = None
[docs]
class FirefoxHistoryPlugin(interface.SQLitePlugin):
"""SQLite parser plugin for Mozilla Firefox history database files.
The Mozilla Firefox history database file is typically stored in:
places.sqlite
"""
NAME = "firefox_history"
DATA_FORMAT = "Mozilla Firefox history SQLite database (places.sqlite) file"
REQUIRED_STRUCTURE = {
"moz_places": frozenset(
["url", "title", "visit_count", "rev_host", "hidden", "typed", "id"]
),
"moz_historyvisits": frozenset(
["id", "visit_date", "from_visit", "visit_type", "place_id"]
),
"moz_bookmarks": frozenset(
["type", "title", "dateAdded", "lastModified", "id", "fk"]
),
"moz_items_annos": frozenset(
["content", "dateAdded", "lastModified", "id", "item_id"]
),
}
QUERIES = [
(
(
"SELECT moz_historyvisits.id, moz_places.url, moz_places.title, "
"moz_places.visit_count, moz_historyvisits.visit_date, "
"moz_historyvisits.from_visit, moz_places.rev_host, "
"moz_places.hidden, moz_places.typed, moz_historyvisits.visit_type "
"FROM moz_places, moz_historyvisits "
"WHERE moz_places.id = moz_historyvisits.place_id"
),
"ParsePageVisitedRow",
),
(
(
"SELECT moz_bookmarks.type, moz_bookmarks.title AS bookmark_title, "
"moz_bookmarks.dateAdded, moz_bookmarks.lastModified, "
"moz_places.url, moz_places.title AS places_title, "
"moz_places.rev_host, moz_places.visit_count, moz_bookmarks.id "
"FROM moz_places, moz_bookmarks "
"WHERE moz_bookmarks.fk = moz_places.id AND moz_bookmarks.type <> 3"
),
"ParseBookmarkRow",
),
(
(
"SELECT moz_items_annos.content, moz_items_annos.dateAdded, "
"moz_items_annos.lastModified, moz_bookmarks.title, "
"moz_places.url, moz_places.rev_host, moz_items_annos.id "
"FROM moz_items_annos, moz_bookmarks, moz_places "
"WHERE moz_items_annos.item_id = moz_bookmarks.id "
"AND moz_bookmarks.fk = moz_places.id"
),
"ParseBookmarkAnnotationRow",
),
(
(
"SELECT moz_bookmarks.id, moz_bookmarks.title,"
"moz_bookmarks.dateAdded, moz_bookmarks.lastModified "
"FROM moz_bookmarks WHERE moz_bookmarks.type = 2"
),
"ParseBookmarkFolderRow",
),
]
_SCHEMA_V24 = {
"moz_anno_attributes": (
"CREATE TABLE moz_anno_attributes ( id INTEGER PRIMARY KEY, name "
"VARCHAR(32) UNIQUE NOT NULL)"
),
"moz_annos": (
"CREATE TABLE moz_annos ( id INTEGER PRIMARY KEY, place_id INTEGER "
"NOT NULL, anno_attribute_id INTEGER, mime_type VARCHAR(32) DEFAULT "
"NULL, content LONGVARCHAR, flags INTEGER DEFAULT 0, expiration "
"INTEGER DEFAULT 0, type INTEGER DEFAULT 0, dateAdded INTEGER "
"DEFAULT 0, lastModified INTEGER DEFAULT 0)"
),
"moz_bookmarks": (
"CREATE TABLE moz_bookmarks ( id INTEGER PRIMARY KEY, type INTEGER, "
"fk INTEGER DEFAULT NULL, parent INTEGER, position INTEGER, title "
"LONGVARCHAR, keyword_id INTEGER, folder_type TEXT, dateAdded "
"INTEGER, lastModified INTEGER)"
),
"moz_bookmarks_roots": (
"CREATE TABLE moz_bookmarks_roots ( root_name VARCHAR(16) UNIQUE, "
"folder_id INTEGER)"
),
"moz_favicons": (
"CREATE TABLE moz_favicons ( id INTEGER PRIMARY KEY, url "
"LONGVARCHAR UNIQUE, data BLOB, mime_type VARCHAR(32), expiration "
"LONG)"
),
"moz_historyvisits": (
"CREATE TABLE moz_historyvisits ( id INTEGER PRIMARY KEY, "
"from_visit INTEGER, place_id INTEGER, visit_date INTEGER, "
"visit_type INTEGER, session INTEGER)"
),
"moz_inputhistory": (
"CREATE TABLE moz_inputhistory ( place_id INTEGER NOT NULL, input "
"LONGVARCHAR NOT NULL, use_count INTEGER, PRIMARY KEY (place_id, "
"input))"
),
"moz_items_annos": (
"CREATE TABLE moz_items_annos ( id INTEGER PRIMARY KEY, item_id "
"INTEGER NOT NULL, anno_attribute_id INTEGER, mime_type VARCHAR(32) "
"DEFAULT NULL, content LONGVARCHAR, flags INTEGER DEFAULT 0, "
"expiration INTEGER DEFAULT 0, type INTEGER DEFAULT 0, dateAdded "
"INTEGER DEFAULT 0, lastModified INTEGER DEFAULT 0)"
),
"moz_keywords": (
"CREATE TABLE moz_keywords ( id INTEGER PRIMARY KEY AUTOINCREMENT, "
"keyword TEXT UNIQUE)"
),
"moz_places": (
"CREATE TABLE moz_places ( id INTEGER PRIMARY KEY, url LONGVARCHAR, "
"title LONGVARCHAR, rev_host LONGVARCHAR, visit_count INTEGER "
"DEFAULT 0, hidden INTEGER DEFAULT 0 NOT NULL, typed INTEGER "
"DEFAULT 0 NOT NULL, favicon_id INTEGER, frecency INTEGER DEFAULT "
"-1 NOT NULL, last_visit_date INTEGER )"
),
}
_SCHEMA_V25 = {
"moz_anno_attributes": (
"CREATE TABLE moz_anno_attributes ( id INTEGER PRIMARY KEY, name "
"VARCHAR(32) UNIQUE NOT NULL)"
),
"moz_annos": (
"CREATE TABLE moz_annos ( id INTEGER PRIMARY KEY, place_id INTEGER "
"NOT NULL, anno_attribute_id INTEGER, mime_type VARCHAR(32) DEFAULT "
"NULL, content LONGVARCHAR, flags INTEGER DEFAULT 0, expiration "
"INTEGER DEFAULT 0, type INTEGER DEFAULT 0, dateAdded INTEGER "
"DEFAULT 0, lastModified INTEGER DEFAULT 0)"
),
"moz_bookmarks": (
"CREATE TABLE moz_bookmarks ( id INTEGER PRIMARY KEY, type INTEGER, "
"fk INTEGER DEFAULT NULL, parent INTEGER, position INTEGER, title "
"LONGVARCHAR, keyword_id INTEGER, folder_type TEXT, dateAdded "
"INTEGER, lastModified INTEGER, guid TEXT)"
),
"moz_bookmarks_roots": (
"CREATE TABLE moz_bookmarks_roots ( root_name VARCHAR(16) UNIQUE, "
"folder_id INTEGER)"
),
"moz_favicons": (
"CREATE TABLE moz_favicons ( id INTEGER PRIMARY KEY, url "
"LONGVARCHAR UNIQUE, data BLOB, mime_type VARCHAR(32), expiration "
"LONG, guid TEXT)"
),
"moz_historyvisits": (
"CREATE TABLE moz_historyvisits ( id INTEGER PRIMARY KEY, "
"from_visit INTEGER, place_id INTEGER, visit_date INTEGER, "
"visit_type INTEGER, session INTEGER)"
),
"moz_hosts": (
"CREATE TABLE moz_hosts ( id INTEGER PRIMARY KEY, host TEXT NOT "
"NULL UNIQUE, frecency INTEGER, typed INTEGER NOT NULL DEFAULT 0, "
"prefix TEXT)"
),
"moz_inputhistory": (
"CREATE TABLE moz_inputhistory ( place_id INTEGER NOT NULL, input "
"LONGVARCHAR NOT NULL, use_count INTEGER, PRIMARY KEY (place_id, "
"input))"
),
"moz_items_annos": (
"CREATE TABLE moz_items_annos ( id INTEGER PRIMARY KEY, item_id "
"INTEGER NOT NULL, anno_attribute_id INTEGER, mime_type VARCHAR(32) "
"DEFAULT NULL, content LONGVARCHAR, flags INTEGER DEFAULT 0, "
"expiration INTEGER DEFAULT 0, type INTEGER DEFAULT 0, dateAdded "
"INTEGER DEFAULT 0, lastModified INTEGER DEFAULT 0)"
),
"moz_keywords": (
"CREATE TABLE moz_keywords ( id INTEGER PRIMARY KEY AUTOINCREMENT, "
"keyword TEXT UNIQUE)"
),
"moz_places": (
"CREATE TABLE moz_places ( id INTEGER PRIMARY KEY, url LONGVARCHAR, "
"title LONGVARCHAR, rev_host LONGVARCHAR, visit_count INTEGER "
"DEFAULT 0, hidden INTEGER DEFAULT 0 NOT NULL, typed INTEGER "
"DEFAULT 0 NOT NULL, favicon_id INTEGER, frecency INTEGER DEFAULT "
"-1 NOT NULL, last_visit_date INTEGER , guid TEXT)"
),
"sqlite_stat1": ("CREATE TABLE sqlite_stat1(tbl, idx, stat)"),
}
SCHEMAS = [_SCHEMA_V24, _SCHEMA_V25]
URL_CACHE_QUERY = (
"SELECT h.id AS id, p.url, p.rev_host FROM moz_places p, "
"moz_historyvisits h WHERE p.id = h.place_id"
)
def _GetUrl(self, url_id, cache, database):
"""Retrieves a URL from a reference to an entry in the from_visit table.
Args:
url_id (str): identifier of the visited URL.
cache (SQLiteCache): cache.
database (SQLiteDatabase): database.
Returns:
str: URL and hostname.
"""
url_cache_results = cache.GetResults("url")
if not url_cache_results:
result_set = database.Query(self.URL_CACHE_QUERY)
cache.CacheQueryResults(result_set, "url", "id", ("url", "rev_host"))
url_cache_results = cache.GetResults("url")
url, reverse_host = url_cache_results.get(url_id, ["", ""])
if not url:
return ""
hostname = self._ReverseHostname(reverse_host)
return f"{url:s} ({hostname:s})"
def _ReverseHostname(self, hostname):
"""Reverses the hostname and strips the leading dot.
The hostname entry is reversed:
moc.elgoog.www.
Should be:
www.google.com
Args:
hostname (str): reversed hostname.
Returns:
str: hostname without a leading dot.
"""
if not hostname:
return ""
if len(hostname) <= 1:
return hostname
if hostname[-1] == ".":
return hostname[::-1][1:]
return hostname[::-1][0:]
[docs]
def ParseBookmarkAnnotationRow(self, parser_mediator, query, row, **unused_kwargs):
"""Parses a bookmark annotation 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)
event_data = FirefoxPlacesBookmarkAnnotationEventData()
event_data.added_time = self._GetPosixTimeInMicrosecondsRowValue(
query_hash, row, "dateAdded"
)
event_data.content = self._GetRowValue(query_hash, row, "content")
event_data.modification_time = self._GetPosixTimeInMicrosecondsRowValue(
query_hash, row, "lastModified"
)
event_data.offset = self._GetRowValue(query_hash, row, "id")
event_data.query = query
event_data.title = self._GetRowValue(query_hash, row, "title")
event_data.url = self._GetRowValue(query_hash, row, "url")
parser_mediator.ProduceEventData(event_data)
[docs]
def ParseBookmarkFolderRow(self, parser_mediator, query, row, **unused_kwargs):
"""Parses a bookmark folder 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)
title = self._GetRowValue(query_hash, row, "title")
event_data = FirefoxPlacesBookmarkFolderEventData()
event_data.added_time = self._GetPosixTimeInMicrosecondsRowValue(
query_hash, row, "dateAdded"
)
event_data.modification_time = self._GetPosixTimeInMicrosecondsRowValue(
query_hash, row, "lastModified"
)
event_data.offset = self._GetRowValue(query_hash, row, "id")
event_data.query = query
event_data.title = title or "N/A"
parser_mediator.ProduceEventData(event_data)
[docs]
def ParseBookmarkRow(self, parser_mediator, query, row, **unused_kwargs):
"""Parses a bookmark 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)
event_data = FirefoxPlacesBookmarkEventData()
event_data.added_time = self._GetPosixTimeInMicrosecondsRowValue(
query_hash, row, "dateAdded"
)
event_data.bookmark_type = self._GetRowValue(query_hash, row, "type")
event_data.host = self._GetRowValue(query_hash, row, "rev_host")
event_data.modification_time = self._GetPosixTimeInMicrosecondsRowValue(
query_hash, row, "lastModified"
)
event_data.offset = self._GetRowValue(query_hash, row, "id")
event_data.places_title = self._GetRowValue(query_hash, row, "places_title")
event_data.query = query
event_data.title = self._GetRowValue(query_hash, row, "bookmark_title")
event_data.url = self._GetRowValue(query_hash, row, "url")
event_data.visit_count = self._GetRowValue(query_hash, row, "visit_count")
parser_mediator.ProduceEventData(event_data)
[docs]
def ParsePageVisitedRow(
self, parser_mediator, query, row, cache=None, database=None, **unused_kwargs
):
"""Parses a page 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.
cache (Optional[SQLiteCache]): cache.
database (Optional[SQLiteDatabase]): database.
"""
query_hash = hash(query)
from_visit = self._GetRowValue(query_hash, row, "from_visit")
if from_visit is not None:
from_visit = self._GetUrl(from_visit, cache, database)
rev_host = self._GetRowValue(query_hash, row, "rev_host")
event_data = FirefoxPlacesPageVisitedEventData()
event_data.from_visit = from_visit
event_data.hidden = self._GetRowValue(query_hash, row, "hidden")
event_data.host = self._ReverseHostname(rev_host)
event_data.last_visited_time = self._GetPosixTimeInMicrosecondsRowValue(
query_hash, row, "visit_date"
)
event_data.offset = self._GetRowValue(query_hash, row, "id")
event_data.query = query
event_data.title = self._GetRowValue(query_hash, row, "title")
event_data.typed = self._GetRowValue(query_hash, row, "typed")
event_data.url = self._GetRowValue(query_hash, row, "url")
event_data.visit_count = self._GetRowValue(query_hash, row, "visit_count")
event_data.visit_type = self._GetRowValue(query_hash, row, "visit_type")
parser_mediator.ProduceEventData(event_data)
sqlite.SQLiteParser.RegisterPlugin(FirefoxHistoryPlugin)