"""Text parser plugin for viminfo files."""
import pyparsing
from dfdatetime import posix_time as dfdatetime_posix_time
from plaso.containers import events
from plaso.lib import errors
from plaso.parsers import text_parser
from plaso.parsers.text_plugins import interface
[docs]
class VimInfoEventData(events.EventData):
"""VimInfo event data.
Attributes:
filename (str): the name of the file that was opened/edited.
history_type (str): the Vim history type.
history_value (str): the Vim history value.
item_number (int): the item number of the history type.
recorded_time (dfdatetime.DateTimeValues): date and time the log entry
was recorded.
"""
DATA_TYPE = "viminfo:history"
[docs]
def __init__(self):
"""Initializes event data."""
super().__init__(data_type=self.DATA_TYPE)
self.filename = None
self.history_type = None
self.history_value = None
self.item_number = None
self.recorded_time = None
[docs]
class VimInfoTextPlugin(
interface.TextPlugin,
):
"""Parses events from Viminfo files."""
NAME = "viminfo"
DATA_FORMAT = "Viminfo file"
ENCODING = "utf-8"
_FILENAME = ".viminfo"
_INTEGER = pyparsing.Word(pyparsing.nums).set_parse_action(
lambda tokens: int(tokens[0], 10)
)
_END_OF_LINE = pyparsing.Suppress(pyparsing.LineEnd())
_HEADER_1 = (
pyparsing.Literal("# This viminfo file was generated by Vim ")
+ pyparsing.Word(pyparsing.nums + ".")
+ _END_OF_LINE
)
_HEADER_2 = (
pyparsing.Literal("# You may edit it if ")
+ (pyparsing.Literal("you're careful!") ^ pyparsing.Literal("you are careful!"))
+ _END_OF_LINE
)
_VERSION_HEADER = pyparsing.Literal("# Viminfo version") + _END_OF_LINE
_VERSION_VALUE = (
pyparsing.Literal("|")
+ pyparsing.Word(pyparsing.nums + ",").set_results_name("version")
+ _END_OF_LINE
)
_ENCODING_HEADER = (
pyparsing.Literal("# Value of 'encoding' when this file was written")
+ _END_OF_LINE
)
_ENCODING_VALUE = (
pyparsing.Literal("*")
+ pyparsing.Literal("encoding=")
+ pyparsing.Word(pyparsing.alphanums + "-").set_results_name("encoding")
+ _END_OF_LINE
)
_PREAMBLE = (
_HEADER_1
+ _HEADER_2
+ _VERSION_HEADER
+ _VERSION_VALUE
+ _ENCODING_HEADER
+ _ENCODING_VALUE
)
_HLSEARCH = (
pyparsing.Literal("# hlsearch on (H) or off (h):")
+ _END_OF_LINE
+ pyparsing.Word("~/hH").set_results_name("hlsearch")
+ _END_OF_LINE
)
# TODO: https://github.com/vim/vim/blob/master/src/viminfo.c#L1525
_SEARCH_PATTERN = (
pyparsing.Literal("# Last Search Pattern:")
+ _END_OF_LINE
+ pyparsing.restOfLine().set_results_name("search_pattern")
+ _END_OF_LINE
+ _END_OF_LINE
)
# TODO: https://github.com/vim/vim/blob/master/src/viminfo.c#L1525
_SUBSTITUTE_SEARCH_PATTERN = (
pyparsing.Literal("# Last Substitute Search Pattern:")
+ _END_OF_LINE
+ pyparsing.restOfLine().set_results_name("substitute_search_pattern")
+ _END_OF_LINE
+ _END_OF_LINE
)
_SUBSTITUTE_STRING = (
pyparsing.Literal("# Last Substitute String:")
+ _END_OF_LINE
+ pyparsing.restOfLine().set_results_name("last_substitute_string")
+ _END_OF_LINE
+ _END_OF_LINE
)
_BAR_ITEM = (
pyparsing.Literal("|")
+ pyparsing.Word(pyparsing.nums, exact=1)
+ pyparsing.Suppress(",")
+ pyparsing.Word(pyparsing.nums, exact=1)
+ pyparsing.Suppress(",")
+ pyparsing.Word(pyparsing.nums, exact=10)
+ pyparsing.Suppress(",")
+ pyparsing.Optional(_INTEGER)
+ pyparsing.Suppress(",")
+ pyparsing.restOfLine()
+ _END_OF_LINE
)
_REGISTER_CONTINUATION = (
pyparsing.Literal('|<"') + pyparsing.restOfLine() + _END_OF_LINE
)
_REGISTER_ITEM = (
pyparsing.Literal("|")
+ _INTEGER
+ pyparsing.Suppress(",")
+ _INTEGER
+ pyparsing.Suppress(",")
+ _INTEGER
+ pyparsing.Suppress(",")
+ _INTEGER
+ pyparsing.Suppress(",")
+ _INTEGER
+ pyparsing.Suppress(",")
+ _INTEGER
+ pyparsing.Suppress(",")
+ pyparsing.Word(pyparsing.nums, exact=10)
+ pyparsing.Suppress(",")
+ pyparsing.Group(
pyparsing.restOfLine()
+ _END_OF_LINE
+ pyparsing.ZeroOrMore(_REGISTER_CONTINUATION)
)
)
_FILEMARK_ITEM = pyparsing.Group(
pyparsing.Literal("|")
+ _INTEGER
+ pyparsing.Suppress(",")
+ _INTEGER
+ pyparsing.Suppress(",")
+ _INTEGER
+ pyparsing.Suppress(",")
+ _INTEGER
+ pyparsing.Suppress(",")
+ pyparsing.Word(pyparsing.nums, exact=10)
+ pyparsing.Suppress(",")
+ pyparsing.restOfLine()
+ _END_OF_LINE
)
_COMMAND_LINE_ITEM = pyparsing.Group(
pyparsing.Literal(":") + pyparsing.restOfLine() + _END_OF_LINE + _BAR_ITEM
).set_results_name("command_line_items*")
_COMMAND_LINE_HISTORY = (
pyparsing.Literal("# Command Line History (newest to oldest):")
+ _END_OF_LINE
+ pyparsing.ZeroOrMore(_COMMAND_LINE_ITEM)
+ _END_OF_LINE
)
_SEARCH_STRING_ITEM = pyparsing.Group(
pyparsing.Literal("?") + pyparsing.restOfLine() + _END_OF_LINE + _BAR_ITEM
).set_results_name("search_string_items*")
_SEARCH_STRING_HISTORY = (
pyparsing.Literal("# Search String History (newest to oldest):")
+ _END_OF_LINE
+ pyparsing.ZeroOrMore(_SEARCH_STRING_ITEM)
+ _END_OF_LINE
)
_EXPRESSION_ITEM = pyparsing.Group(
pyparsing.Literal("=")
+ pyparsing.Word(pyparsing.alphas + "/:")
+ _END_OF_LINE
+ _BAR_ITEM
).set_results_name("expression_history_items*")
_EXPRESSION_HISTORY = (
pyparsing.Literal("# Expression History (newest to oldest):")
+ _END_OF_LINE
+ pyparsing.ZeroOrMore(_EXPRESSION_ITEM)
)
_INPUT_LINE_ITEM = pyparsing.Group(
pyparsing.Literal("@")
+ pyparsing.Word(pyparsing.alphas + "/:")
+ _END_OF_LINE
+ _BAR_ITEM
).set_results_name("input_line_history_items*")
_INPUT_LINE_HISTORY = (
pyparsing.Literal("# Input Line History (newest to oldest):")
+ _END_OF_LINE
+ pyparsing.ZeroOrMore(_INPUT_LINE_ITEM)
)
_DEBUG_LINE_ITEM = pyparsing.Group(
pyparsing.Literal("@")
+ pyparsing.Word(pyparsing.alphas + "/:")
+ _END_OF_LINE
+ _BAR_ITEM
).set_results_name("debug_line_history_items*")
_DEBUG_LINE_HISTORY = (
pyparsing.Literal("# Debug Line History (newest to oldest):")
+ _END_OF_LINE
+ pyparsing.ZeroOrMore(_DEBUG_LINE_ITEM)
)
_REGISTERS_CONTENT = (
pyparsing.Suppress(pyparsing.White("\t"))
+ pyparsing.restOfLine()
+ _END_OF_LINE
)
# http://vimdoc.sourceforge.net/htmldoc/change.html#registers
_REGISTERS_ITEM = pyparsing.Group(
pyparsing.Literal('"')
+ pyparsing.Or([_INTEGER, pyparsing.Word(pyparsing.printables)])
+ pyparsing.Suppress(pyparsing.White("\t"))
+ pyparsing.one_of(["BLOCK", "CHAR", "LINE"])
+ pyparsing.Suppress(pyparsing.White("\t"))
+ _INTEGER
+ _END_OF_LINE
+ pyparsing.Group(pyparsing.ZeroOrMore(_REGISTERS_CONTENT))
+ _REGISTER_ITEM
).set_results_name("registers_items*")
_REGISTERS_HISTORY = (
pyparsing.Literal("# Registers:")
+ _END_OF_LINE
+ pyparsing.ZeroOrMore(_REGISTERS_ITEM)
)
_FILEMARKS_ITEM = pyparsing.Group(
pyparsing.Literal("'")
+ _INTEGER
+ _INTEGER
+ _INTEGER
+ pyparsing.restOfLine()
+ _END_OF_LINE
+ _FILEMARK_ITEM
).set_results_name("filemarks_items*")
_FILEMARKS_HISTORY = (
pyparsing.Literal("# File marks:")
+ _END_OF_LINE
+ pyparsing.ZeroOrMore(_FILEMARKS_ITEM)
+ _END_OF_LINE
)
_JUMPLIST_ITEM = pyparsing.Group(
pyparsing.Word("-'")
+ _INTEGER
+ _INTEGER
+ pyparsing.restOfLine()
+ _END_OF_LINE
+ _FILEMARK_ITEM
).set_results_name("jumplist_items*")
_JUMPLIST_HISTORY = (
pyparsing.Literal("# Jumplist (newest first):")
+ _END_OF_LINE
+ pyparsing.ZeroOrMore(_JUMPLIST_ITEM)
+ _END_OF_LINE
)
_HISTORY_MARKS_HISTORY = (
pyparsing.Literal("# History of marks within files (newest to oldest):")
+ _END_OF_LINE
)
_LINE_STRUCTURES = [
("command_line_history", _COMMAND_LINE_HISTORY),
("debug_line_history", _DEBUG_LINE_HISTORY),
("expression_history", _EXPRESSION_HISTORY),
("filemarks_history", _FILEMARKS_HISTORY),
("history_marks_history", _HISTORY_MARKS_HISTORY),
("hlsearch", _HLSEARCH),
("input_line_history", _INPUT_LINE_HISTORY),
("jumplist_history", _JUMPLIST_HISTORY),
("preamble", _PREAMBLE),
("registers_history", _REGISTERS_HISTORY),
("search_pattern", _SEARCH_PATTERN),
("search_string_history", _SEARCH_STRING_HISTORY),
("substitute_search", _SUBSTITUTE_SEARCH_PATTERN),
("substitute_string", _SUBSTITUTE_STRING),
]
VERIFICATION_GRAMMAR = _PREAMBLE
def _ParseCommandLineHistory(self, parser_mediator, structure):
"""Parses command line history items and creates VimInfoEventData objects.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
structure (pyparsing.ParseResults): structure of tokens derived from
lines of text that match a command line history
"""
for index, item in enumerate(structure.get("command_line_items", [])):
event_data = VimInfoEventData()
event_data.history_value = item[1]
event_data.history_type = "Command Line History"
event_data.item_number = index
event_data.recorded_time = dfdatetime_posix_time.PosixTime(
timestamp=item[5]
)
parser_mediator.ProduceEventData(event_data)
def _ParseSearchStringHistory(self, parser_mediator, structure):
"""Parses search string history items and creates VimInfoEventData objects.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
structure (pyparsing.ParseResults): structure of tokens derived from
lines of text that match a search string history
"""
for index, item in enumerate(structure.get("search_string_items", [])):
event_data = VimInfoEventData()
event_data.history_value = item[1]
event_data.history_type = "Search String History"
event_data.item_number = index
event_data.recorded_time = dfdatetime_posix_time.PosixTime(
timestamp=item[5]
)
parser_mediator.ProduceEventData(event_data)
def _ParseExpressionHistory(self, parser_mediator, structure):
"""Parses expression history items and creates VimInfoEventData objects.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
structure (pyparsing.ParseResults): structure of tokens derived from
lines of text that match an expression history
"""
for index, item in enumerate(structure.get("expression_history_items", [])):
event_data = VimInfoEventData()
event_data.history_value = item[1]
event_data.history_type = "Expression History"
event_data.item_number = index
event_data.recorded_time = dfdatetime_posix_time.PosixTime(
timestamp=item[5]
)
parser_mediator.ProduceEventData(event_data)
def _ParseInputLineHistory(self, parser_mediator, structure):
"""Parses input line history items and creates VimInfoEventData objects.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
structure (pyparsing.ParseResults): structure of tokens derived from
lines of text that match an input line history
"""
for index, item in enumerate(structure.get("input_line_history_items", [])):
event_data = VimInfoEventData()
event_data.history_value = item[1]
event_data.history_type = "Input Line History"
event_data.item_number = index
event_data.recorded_time = dfdatetime_posix_time.PosixTime(
timestamp=item[5]
)
parser_mediator.ProduceEventData(event_data)
def _ParseDebugLineHistory(self, parser_mediator, structure):
"""Parses debug line history items and creates VimInfoEventData objects.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
structure (pyparsing.ParseResults): structure of tokens derived from
lines of text that match a debug line history
"""
for index, item in enumerate(structure.get("debug_line_history_items", [])):
event_data = VimInfoEventData()
event_data.history_value = item[1]
event_data.history_type = "Debug Line History"
event_data.item_number = index
event_data.recorded_time = dfdatetime_posix_time.PosixTime(
timestamp=item[5]
)
parser_mediator.ProduceEventData(event_data)
def _ParseRegistersHistory(self, parser_mediator, structure):
"""Parses register history items and creates VimInfoEventData objects.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
structure (pyparsing.ParseResults): structure of tokens derived from
lines of text that match a Register history
"""
for item in structure.get("registers_items", []):
event_data = VimInfoEventData()
event_data.history_value = "\n".join(item[4])
event_data.history_type = "Register"
event_data.item_number = item[1]
event_data.recorded_time = dfdatetime_posix_time.PosixTime(
timestamp=item[12]
)
parser_mediator.ProduceEventData(event_data)
def _ParseFilemarksHistory(self, parser_mediator, structure):
"""Parses filemarks history items and creates VimInfoEventData objects.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
structure (pyparsing.ParseResults): structure of tokens derived from
lines of text that match a Filemarks history
"""
for index, item in enumerate(structure.get("filemarks_items", [])):
event_data = VimInfoEventData()
event_data.filename = item[4].strip()
event_data.history_type = "File mark"
event_data.item_number = index
event_data.recorded_time = dfdatetime_posix_time.PosixTime(
timestamp=item[5][5]
)
parser_mediator.ProduceEventData(event_data)
def _ParseJumplistHistory(self, parser_mediator, structure):
"""Parses jumplist items and creates VimInfoEventData objects.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
structure (pyparsing.ParseResults): structure of tokens derived from
lines of text that match a JumpList history
"""
for index, item in enumerate(structure.get("jumplist_items", [])):
event_data = VimInfoEventData()
event_data.filename = item[3].strip()
event_data.history_type = "Jumplist"
event_data.item_number = index
event_data.recorded_time = dfdatetime_posix_time.PosixTime(
timestamp=item[4][5]
)
parser_mediator.ProduceEventData(event_data)
def _ParseRecord(self, parser_mediator, key, structure):
"""Parses a pyparsing structure.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
key (str): name of the parsed structure.
structure (pyparsing.ParseResults): tokens from a parsed log line.
Raises:
ParseError: if the structure cannot be parsed.
"""
if key == "command_line_history":
self._ParseCommandLineHistory(parser_mediator, structure)
elif key == "search_string_history":
self._ParseSearchStringHistory(parser_mediator, structure)
elif key == "expression_history":
self._ParseExpressionHistory(parser_mediator, structure)
elif key == "input_line_history":
self._ParseInputLineHistory(parser_mediator, structure)
elif key == "debug_line_history":
self._ParseDebugLineHistory(parser_mediator, structure)
elif key == "registers_history":
self._ParseRegistersHistory(parser_mediator, structure)
elif key == "filemarks_history":
self._ParseFilemarksHistory(parser_mediator, structure)
elif key == "jumplist_history":
self._ParseJumplistHistory(parser_mediator, structure)
# TODO(sydp): add support for history marks history lines
# elif key == 'history_marks_history':
# self._ParseHistoryMarksHistory(self, parser_mediator, structure)
[docs]
def CheckRequiredFormat(self, parser_mediator, text_reader):
"""Check if the log record has the minimal structure required by the parser.
Args:
parser_mediator (ParserMediator): mediates interactions between parsers
and other components, such as storage and dfVFS.
text_reader (EncodedTextReader): text reader.
Returns:
bool: True if this is the correct plugin, False otherwise.
"""
try:
self._VerifyString(text_reader.lines)
except errors.ParseError:
return False
return True
text_parser.TextLogParser.RegisterPlugin(VimInfoTextPlugin)