#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright 2014 The Plaso Project Authors. # Please see the AUTHORS file for details on individual authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """This file contains SkyDrive error log file parser in plaso.""" import logging import pyparsing from plaso.events import time_events from plaso.lib import eventdata from plaso.lib import timelib from plaso.parsers import manager from plaso.parsers import text_parser __author__ = 'Francesco Picasso (francesco.picasso@gmail.com)' class SkyDriveLogErrorEvent(time_events.TimestampEvent): """Convenience class for a SkyDrive error log line event.""" DATA_TYPE = 'skydrive:error:line' def __init__(self, timestamp, module, source_code, text, detail): """Initializes the event object. Args: timestamp: Milliseconds since epoch in UTC. module: The module name that generated the log line. source_code: Logging source file and line number. text: The error text message. detail: The error details. """ super(SkyDriveLogErrorEvent, self).__init__( timestamp, eventdata.EventTimestamp.ADDED_TIME) self.module = module self.source_code = source_code self.text = text self.detail = detail class SkyDriveLogErrorParser(text_parser.PyparsingMultiLineTextParser): """Parse SkyDrive error log files.""" NAME = 'skydrive_log_error' DESCRIPTION = u'Parser for OneDrive (or SkyDrive) error log files.' ENCODING = 'utf-8' # Common SDE (SkyDriveError) structures. INTEGER_CAST = text_parser.PyParseIntCast HYPHEN = text_parser.PyparsingConstants.HYPHEN TWO_DIGITS = text_parser.PyparsingConstants.TWO_DIGITS TIME_MSEC = text_parser.PyparsingConstants.TIME_MSEC MSEC = pyparsing.Word(pyparsing.nums, max=3).setParseAction(INTEGER_CAST) COMMA = pyparsing.Literal(u',').suppress() DOT = pyparsing.Literal(u'.').suppress() IGNORE_FIELD = pyparsing.CharsNotIn(u',').suppress() # Header line timestamp (2013-07-25-160323.291). SDE_HEADER_TIMESTAMP = pyparsing.Group( text_parser.PyparsingConstants.DATE.setResultsName('date') + HYPHEN + TWO_DIGITS.setResultsName('hh') + TWO_DIGITS.setResultsName('mm') + TWO_DIGITS.setResultsName('ss') + DOT + MSEC.setResultsName('ms')).setResultsName('hdr_timestamp') # Line timestamp (07-25-13,16:06:31.820). SDE_TIMESTAMP = ( TWO_DIGITS.setResultsName('month') + HYPHEN + TWO_DIGITS.setResultsName('day') + HYPHEN + TWO_DIGITS.setResultsName('year_short') + COMMA + TIME_MSEC.setResultsName('time')).setResultsName('timestamp') # Header start. SDE_HEADER_START = ( pyparsing.Literal(u'######').suppress() + pyparsing.Literal(u'Logging started.').setResultsName('log_start')) # Multiline entry end marker, matched from right to left. SDE_ENTRY_END = pyparsing.StringEnd() | SDE_HEADER_START | SDE_TIMESTAMP # SkyDriveError line pyparsing structure. SDE_LINE = ( SDE_TIMESTAMP + COMMA + IGNORE_FIELD + COMMA + IGNORE_FIELD + COMMA + IGNORE_FIELD + COMMA + pyparsing.CharsNotIn(u',').setResultsName('module') + COMMA + pyparsing.CharsNotIn(u',').setResultsName('source_code') + COMMA + IGNORE_FIELD + COMMA + IGNORE_FIELD + COMMA + IGNORE_FIELD + COMMA + pyparsing.Optional(pyparsing.CharsNotIn(u',').setResultsName('text')) + COMMA + pyparsing.SkipTo(SDE_ENTRY_END).setResultsName('detail') + pyparsing.lineEnd()) # SkyDriveError header pyparsing structure. SDE_HEADER = ( SDE_HEADER_START + pyparsing.Literal(u'Version=').setResultsName('ver_str') + pyparsing.Word(pyparsing.nums + u'.').setResultsName('ver_num') + pyparsing.Literal(u'StartSystemTime:').suppress() + SDE_HEADER_TIMESTAMP + pyparsing.Literal(u'StartLocalTime:').setResultsName('lt_str') + pyparsing.SkipTo(pyparsing.lineEnd()).setResultsName('details') + pyparsing.lineEnd()) # Define the available log line structures. LINE_STRUCTURES = [ ('logline', SDE_LINE), ('header', SDE_HEADER) ] def __init__(self): """Initializes a parser object.""" super(SkyDriveLogErrorParser, self).__init__() self.use_local_zone = False def VerifyStructure(self, parser_context, line): """Verify that this file is a SkyDrive Error log file. Args: parser_context: A parser context object (instance of ParserContext). line: A single line from the text file. Returns: True if this is the correct parser, False otherwise. """ try: parsed_structure = self.SDE_HEADER.parseString(line) except pyparsing.ParseException: logging.debug(u'Not a SkyDrive Error log file') return False timestamp = self._GetTimestampFromHeader(parsed_structure.hdr_timestamp) if not timestamp: logging.debug( u'Not a SkyDrive Error log file, invalid timestamp {0:s}'.format( parsed_structure.timestamp)) return False return True def ParseRecord(self, parser_context, key, structure): """Parse each record structure and return an EventObject if applicable. Args: parser_context: A parser context object (instance of ParserContext). key: An identification string indicating the name of the parsed structure. structure: A pyparsing.ParseResults object from a line in the log file. Returns: An event object (instance of EventObject) or None. """ if key == 'logline': return self._ParseLine(structure) elif key == 'header': return self._ParseHeader(structure) else: logging.warning( u'Unable to parse record, unknown structure: {0:s}'.format(key)) def _ParseLine(self, structure): """Parse a logline and store appropriate attributes.""" timestamp = self._GetTimestampFromLine(structure.timestamp) if not timestamp: logging.debug(u'SkyDriveLogError invalid timestamp {0:s}'.format( structure.timestamp)) return # Replace newlines with spaces in structure.detail to preserve output. return SkyDriveLogErrorEvent( timestamp, structure.module, structure.source_code, structure.text, structure.detail.replace(u'\n', u' ')) def _ParseHeader(self, structure): """Parse header lines and store appropriate attributes. [u'Logging started.', u'Version=', u'17.0.2011.0627', [2013, 7, 25], 16, 3, 23, 291, u'StartLocalTime', u'
'] Args: structure: The parsed structure. Returns: timestamp: The event or none. """ timestamp = self._GetTimestampFromHeader(structure.hdr_timestamp) if not timestamp: logging.debug( u'SkyDriveLogError invalid timestamp {0:d}'.format( structure.hdr_timestamp)) return text = u'{0:s} {1:s} {2:s}'.format( structure.log_start, structure.ver_str, structure.ver_num) detail = u'{0:s} {1:s}'.format(structure.lt_str, structure.details) return SkyDriveLogErrorEvent( timestamp, None, None, text, detail) def _GetTimestampFromHeader(self, structure): """Gets a timestamp from the structure. The following is an example of the timestamp structure expected [[2013, 7, 25], 16, 3, 23, 291] Args: structure: The parsed structure, which should be a timestamp. Returns: timestamp: A plaso timelib timestamp event or 0. """ year, month, day = structure.date hour = structure.get('hh', 0) minute = structure.get('mm', 0) second = structure.get('ss', 0) microsecond = structure.get('ms', 0) * 1000 return timelib.Timestamp.FromTimeParts( year, month, day, hour, minute, second, microseconds=microsecond) def _GetTimestampFromLine(self, structure): """Gets a timestamp from string from the structure The following is an example of the timestamp structure expected [7, 25, 13, [16, 3, 24], 649] Args: structure: The parsed structure. Returns: timestamp: A plaso timelib timestamp event or 0. """ hour, minute, second = structure.time[0] microsecond = structure.time[1] * 1000 # TODO: Verify if timestamps are locale dependent. year = structure.get('year_short', 0) month = structure.get('month', 0) day = structure.get('day', 0) if year < 0 or not month or not day: return 0 year += 2000 return timelib.Timestamp.FromTimeParts( year, month, day, hour, minute, second, microseconds=microsecond) manager.ParsersManager.RegisterParser(SkyDriveLogErrorParser)