Source code for plaso.parsers.ios_discord

"""Parser for iOS Discord message (JSON) files."""

import codecs
import json
import os

from dfdatetime import time_elements as dfdatetime_time_elements

from plaso.containers import events
from plaso.lib import errors
from plaso.parsers import interface
from plaso.parsers import manager


[docs] class IOSDiscordMessageEventData(events.EventData): """iOS discord message event data. Attributes: attachment_name (str): The attachment filename. attachment_proxy_urls (str): The attachment proxy URL. attachment_size (int): The attachment size. attachment_type (str): The attachment type. channel_identifier (str): identifier of the user channel. content (str): Message content. edited_timestamp (str): Message edit time. sent_time (dfdatetime.DateTimeValues): Message timestamp. user_identifier (str): ID of the message author. username (str): The username of the message sender. """ DATA_TYPE = "ios:discord:message"
[docs] def __init__(self): """Initializes event data.""" super().__init__(data_type=self.DATA_TYPE) self.attachment_name = None self.attachment_proxy_url = None self.attachment_size = None self.attachment_type = None self.channel_identifier = None self.content = None self.sent_time = None self.user_identifier = None self.username = None
[docs] class IOSDiscordParser(interface.FileObjectParser): """Parses iOS discord message files.""" NAME = "discord_ios" DATA_FORMAT = "iOS discord message" REQUIRED_MESSAGE_KEYS = frozenset( ["attachments", "author", "channel_id", "content", "timestamp"] ) _ENCODING = "utf-8" _MAXIMUM_FILE_SIZE = 16 * 1024 * 1024 def _GetDateTimeValue(self, parser_mediator, timestamp): """Retrieves a date and time value. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfVFS. timestamp (Optional[str]): timestamp value. Returns: dfdatetime.TimeElementsInMicroseconds: date and time value or None if not available. """ if timestamp is None: return None try: date_time = dfdatetime_time_elements.TimeElementsInMicroseconds() date_time.CopyFromStringISO8601(timestamp) except ValueError as exception: parser_mediator.ProduceExtractionWarning( f"Unable to parse timestamp value: {timestamp:s} with error: " f"{exception!s}" ) date_time = None return date_time
[docs] def ParseFileObject(self, parser_mediator, file_object): """Parses a iOS discord message file.""" # First check for initial 2 characters being open brace and open list. if file_object.read(2) != b"[{": display_name = parser_mediator.GetDisplayName() raise errors.WrongParser( f"[{self.NAME!s}] {display_name!s} is not a valid Discord messages " f"file, missing opening brace and list" ) file_object.seek(0, os.SEEK_SET) # Note that _MAXIMUM_FILE_SIZE prevents this read to become too large. file_content = file_object.read() try: file_content = codecs.decode(file_content, self._ENCODING) except (UnicodeDecodeError, json.JSONDecodeError): display_name = parser_mediator.GetDisplayName() raise errors.WrongParser( f"[{self.NAME!s}] {display_name!s} is not a valid Discord messages " f"file, unable to decode as UTF-8" ) # Second verify it is valid JSON. try: json_dict = json.loads(file_content) except OSError as exception: display_name = parser_mediator.GetDisplayName() raise errors.WrongParser( f"[{self.NAME!s}] Unable to open file {display_name!s} with error: " f"{exception!s}" ) except ValueError as exception: display_name = parser_mediator.GetDisplayName() raise errors.WrongParser( f"[{self.NAME!s}] {display_name!s} is not a valid Discord messages " f"file, unable to decode as JSON with error: {exception!s}" ) # Third verify the file has the correct keys for a Discord messages file. messages = json_dict or [{}] if not set(self.REQUIRED_MESSAGE_KEYS).issubset(set(messages[0].keys())): raise errors.WrongParser("File does not contain Discord messages data") for message in messages: attachments = message.get("attachments") or [{}] timestamp = message.get("timestamp") event_data = IOSDiscordMessageEventData() event_data.attachment_name = attachments[0].get("filename") or None event_data.attachment_proxy_url = attachments[0].get("proxy_url") or None event_data.attachment_size = attachments[0].get("size") or None event_data.attachment_type = attachments[0].get("content_type") or None event_data.channel_identifier = message.get("channel_id") or None event_data.content = message.get("content") or None event_data.sent_time = self._GetDateTimeValue(parser_mediator, timestamp) event_data.user_identifier = message.get("author", {}).get("id") or None event_data.username = message.get("author", {}).get("username") or None parser_mediator.ProduceEventData(event_data)
manager.ParsersManager.RegisterParser(IOSDiscordParser)