import demistomock as demisto # noqa: F401
from CommonServerPython import * # noqa: F401
#!/usr/bin/env python2
# coding=utf-8
# PEP0263 https://www.python.org/dev/peps/pep-0263/
"""Based on MS-OXMSG protocol specification
ref:https://blogs.msdn.microsoft.com/openspecification/2010/06/20/msg-file-format-rights-managed-email-message-part-2/
ref:https://msdn.microsoft.com/en-us/library/cc463912(v=EXCHG.80).aspx
"""
import codecs
import email
import email.utils
import quopri
import tempfile
import unicodedata
from base64 import b64decode
from email import encoders, message_from_string
from email.header import Header, decode_header
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.parser import HeaderParser
from email.utils import getaddresses
from struct import unpack
import chardet # type: ignore
from olefile import OleFileIO, isOleFile
reload(sys)
sys.setdefaultencoding('utf8') # pylint: disable=no-member
MAX_DEPTH_CONST = 3
"""
https://github.com/vikramarsid/msg_parser
"""
DATA_TYPE_MAP = {
"0x0000": "PtypUnspecified",
"0x0001": "PtypNull",
"0x0002": "PtypInteger16",
"0x0003": "PtypInteger32",
"0x0004": "PtypFloating32",
"0x0005": "PtypFloating64",
"0x0006": "PtypCurrency",
"0x0007": "PtypFloatingTime",
"0x000A": "PtypErrorCode",
"0x000B": "PtypBoolean",
"0x000D": "PtypObject",
"0x0014": "PtypInteger64",
"0x001E": "PtypString8",
"0x001F": "PtypString",
"0x0040": "PtypTime",
"0x0048": "PtypGuid",
"0x00FB": "PtypServerId",
"0x00FD": "PtypRestriction",
"0x00FE": "PtypRuleAction",
"0x0102": "PtypBinary",
"0x1002": "PtypMultipleInteger16",
"0x1003": "PtypMultipleInteger32",
"0x1004": "PtypMultipleFloating32",
"0x1005": "PtypMultipleFloating64",
"0x1006": "PtypMultipleCurrency",
"0x1007": "PtypMultipleFloatingTime",
"0x1014": "PtypMultipleInteger64",
"0x101F": "PtypMultipleString",
"0x101E": "PtypMultipleString8",
"0x1040": "PtypMultipleTime",
"0x1048": "PtypMultipleGuid",
"0x1102": "PtypMultipleBinary"
}
class DataModel(object):
def __init__(self):
self.data_type_name = None
@staticmethod
def lookup_data_type_name(data_type):
return DATA_TYPE_MAP.get(data_type)
def get_value(self, data_value, data_type_name=None, data_type=None):
if data_type_name:
self.data_type_name = data_type_name
elif data_type:
self.data_type_name = self.lookup_data_type_name(data_type)
else:
raise Exception("required arguments not provided to the constructor of the class.")
if not hasattr(self, self.data_type_name):
return None
value = getattr(self, self.data_type_name)(data_value)
return value
@staticmethod
def PtypUnspecified(data_value):
return data_value
@staticmethod
def PtypNull(data_value):
return None
@staticmethod
def PtypInteger16(data_value):
return int(data_value.encode('hex'), 16)
@staticmethod
def PtypInteger32(data_value):
return int(data_value.encode('hex'), 32)
@staticmethod
def PtypFloating32(data_value):
return unpack('f', data_value)[0]
@staticmethod
def PtypFloating64(data_value):
return unpack('d', data_value)[0]
@staticmethod
def PtypCurrency(data_value):
return data_value
@staticmethod
def PtypFloatingTime(data_value):
return data_value
@staticmethod
def PtypErrorCode(data_value):
return unpack('I', data_value)[0]
@staticmethod
def PtypBoolean(data_value):
return unpack('B', data_value[0])[0] != 0
@staticmethod
def PtypObject(data_value):
if data_value and '\x00' in data_value:
pass
# data_value = data_value.replace('\x00', '')
return data_value
@staticmethod
def PtypInteger64(data_value):
return unpack('q', data_value)[0]
@staticmethod
def PtypString8(data_value):
if data_value and '\x00' in data_value:
data_value = data_value.replace('\x00', '')
return data_value
@staticmethod
def PtypString(data_value):
if data_value:
try:
if USER_ENCODING:
demisto.debug('Using argument user_encoding: {} to decode parsed message.'.format(USER_ENCODING))
return data_value.decode(USER_ENCODING, errors="ignore")
res = chardet.detect(data_value)
enc = res['encoding'] or 'ascii' # in rare cases chardet fails to detect and return None as encoding
if enc != 'ascii':
if enc.lower() == 'windows-1252' and res['confidence'] < 0.9:
enc = DEFAULT_ENCODING if DEFAULT_ENCODING else 'windows-1250'
demisto.debug('Encoding detection confidence below threshold {}, '
'switching encoding to "{}"'.format(res, enc))
temp = data_value
data_value = temp.decode(enc, errors='ignore')
if '\x00' in data_value:
demisto.debug('None bytes found on encoded string, will try use utf-16-le '
'encoding instead')
data_value = temp.decode("utf-16-le", errors="ignore")
elif b'\x00' not in data_value:
data_value = data_value.decode("ascii", errors="ignore")
else:
data_value = data_value.decode("utf-16-le", errors="ignore")
except UnicodeDecodeError:
data_value = data_value.decode("utf-16-le", errors="ignore")
if isinstance(data_value, (bytes, bytearray)):
data_value = data_value.decode('utf-8')
return data_value
@staticmethod
def PtypTime(data_value):
return get_time(data_value)
@staticmethod
def PtypGuid(data_value):
return data_value
@staticmethod
def PtypServerId(data_value):
return data_value
@staticmethod
def PtypRestriction(data_value):
return data_value
@staticmethod
def PtypRuleAction(data_value):
return data_value
@staticmethod
def PtypBinary(data_value):
if data_value and '\x00' in data_value:
data_value = data_value.replace('\x00', '')
return data_value
@staticmethod
def PtypMultipleInteger16(data_value):
entry_count = len(data_value) / 2
return [unpack('h', data_value[i * 2:(i + 1) * 2])[0] for i in range(entry_count)]
@staticmethod
def PtypMultipleInteger32(data_value):
entry_count = len(data_value) / 4
return [unpack('i', data_value[i * 4:(i + 1) * 4])[0] for i in range(entry_count)]
@staticmethod
def PtypMultipleFloating32(data_value):
entry_count = len(data_value) / 4
return [unpack('f', data_value[i * 4:(i + 1) * 4])[0] for i in range(entry_count)]
@staticmethod
def PtypMultipleFloating64(data_value):
entry_count = len(data_value) / 8
return [unpack('d', data_value[i * 8:(i + 1) * 8])[0] for i in range(entry_count)]
@staticmethod
def PtypMultipleCurrency(data_value):
return data_value
@staticmethod
def PtypMultipleFloatingTime(data_value):
entry_count = len(data_value) / 8
return [get_floating_time(data_value[i * 8:(i + 1) * 8]) for i in range(entry_count)]
@staticmethod
def PtypMultipleInteger64(data_value):
entry_count = len(data_value) / 8
return [unpack('q', data_value[i * 8:(i + 1) * 8])[0] for i in range(entry_count)]
@staticmethod
def PtypMultipleString(data_value):
# string_list = []
# for item_bytes in data_value:
# if item_bytes and '\x00' in item_bytes:
# item_bytes = item_bytes.replace('\x00', '')
# string_list.append(item_bytes.decode('utf-16-le'))
return data_value
@staticmethod
def PtypMultipleString8(data_value):
return data_value
@staticmethod
def PtypMultipleTime(data_value):
entry_count = len(data_value) / 8
return [get_time(data_value[i * 8:(i + 1) * 8]) for i in range(entry_count)]
@staticmethod
def PtypMultipleGuid(data_value):
entry_count = len(data_value) / 16
return [data_value[i * 16:(i + 1) * 16] for i in range(entry_count)]
@staticmethod
def PtypMultipleBinary(data_value):
return data_value
def get_floating_time(data_value):
return datetime(
year=1899, month=12, day=30
) + timedelta(
days=unpack('d', data_value)[0]
)
def get_time(data_value):
return datetime(
year=1601, month=1, day=1
) + timedelta(
microseconds=unpack('q', data_value)[0] / 10.0
)
def parse_nesting_level(nesting_level_to_return, output):
# if nesting_level_to_return == 'All files' leave as is
email = output[0]
if nesting_level_to_return == 'Outer file':
# return only the outer email info
output = output[0]
elif nesting_level_to_return == 'Inner file':
# the last file in list it is the inner attached file
email = output[-1]
output = output[-1]
return email, output
def get_multi_value_offsets(data_value):
ul_count = unpack('I', data_value[:4])[0]
if ul_count == 1:
rgul_data_offsets = [8]
else:
rgul_data_offsets = [unpack('Q', data_value[4 + i * 8:4 + (i + 1) * 8])[0] for i in range(ul_count)]
rgul_data_offsets.append(len(data_value))
return ul_count, rgul_data_offsets
class EmailFormatter(object):
def __init__(self, msg_object):
self.msg_obj = msg_object
self.message = MIMEMultipart()
self.message.set_charset('utf-8')
def build_email(self):
# Setting Message ID
self.message.set_param("Message-ID", self.msg_obj.message_id)
# Encoding for unicode subject
self.message['Subject'] = Header(self.msg_obj.subject, charset='UTF-8')
# Setting Date Time
# Returns a date string as specified by RFC 2822, e.g.: Fri, 09 Nov 2001 01:08:47 -0000
self.message['Date'] = str(self.msg_obj.sent_date)
# At least one recipient is required
# Required fromAddress
from_address = flatten_list(self.msg_obj.sender)
if from_address:
self.message['From'] = from_address
to_address = flatten_list(self.msg_obj.header_dict.get("To"))
if to_address:
self.message['To'] = to_address
cc_address = flatten_list(self.msg_obj.header_dict.get("CC"))
if cc_address:
self.message['CC'] = cc_address
bcc_address = flatten_list(self.msg_obj.header_dict.get("BCC"))
if bcc_address:
self.message['BCC'] = bcc_address
# Add reply-to
reply_to = flatten_list(self.msg_obj.reply_to)
if reply_to:
self.message.add_header('reply-to', reply_to)
else:
self.message.add_header('reply-to', from_address)
# Required Email body content
body_content = self.msg_obj.body
if body_content:
if "<html>" in body_content:
body_type = 'html'
else:
body_type = 'plain'
body = MIMEText(_text=body_content, _subtype=body_type, _charset="UTF-8")
self.message.attach(body)
else:
raise KeyError("Missing email body")
# Add message preamble
self.message.preamble = 'You will not see this in a MIME-aware mail reader.\n'
# Optional attachments
attachments = self.msg_obj.attachments
if attachments:
self._process_attachments(self.msg_obj.attachments)
# composed email
composed = self.message.as_string()
return composed
def save_file(self, file_path):
eml_content = self.build_email()
file_name = str(self.message['Subject']) + ".eml"
eml_file_path = os.path.join(file_path, file_name)
with codecs.open(eml_file_path, mode="wb+", encoding="utf-8") as eml_file:
eml_file.write(eml_content.decode("utf-8"))
return eml_file_path
def _process_attachments(self, attachments):
for attachment in attachments:
ctype = attachment.AttachMimeTag
data = attachment.data
filename = attachment.DisplayName
maintype, subtype = ctype.split('/', 1)
if maintype == 'text' or "message" in maintype:
attach = MIMEText(data, _subtype=subtype)
elif maintype == 'image':
attach = MIMEImage(data, _subtype=subtype) # type: ignore[assignment]
elif maintype == 'audio':
attach = MIMEAudio(data, _subtype=subtype) # type: ignore[assignment]
else:
attach = MIMEBase(maintype, subtype) # type: ignore[assignment]
attach.set_payload(data)
# Encode the payload using Base64
encoders.encode_base64(attach)
# Set the filename parameter
base_filename = os.path.basename(filename)
attach.add_header('Content-ID', '<{}>'.format(base_filename))
attach.add_header('Content-Disposition', 'attachment', filename=base_filename)
self.message.attach(attach)
def flatten_list(string_list):
if string_list and isinstance(string_list, list):
string = ",".join(string_list)
return string
return None
def normalize(input_str):
if not input_str:
return input_str
try:
if isinstance(input_str, list):
input_str = [s.decode('ascii') for s in input_str]
else:
input_str.decode('ascii')
return input_str
except UnicodeError:
if not isinstance(input_str, unicode):
input_str = str(input_str).decode("utf-8", "replace")
normalized = unicodedata.normalize('NFKD', input_str)
if not normalized.strip():
normalized = input_str.encode('unicode-escape').decode('utf-8')
return normalized
# coding=utf-8
# autogenerated using ms_props_generator.py
PROPS_ID_MAP = {
"0x0001": {
"data_type": "0x0102",
"name": "TemplateData"
},
"0x0002": {
"data_type": "0x000B",
"name": "AlternateRecipientAllowed"
},
"0x0004": {
"data_type": "0x0102",
"name": "ScriptData"
},
"0x0005": {
"data_type": "0x000B",
"name": "AutoForwarded"
},
"0x000F": {
"data_type": "0x0040",
"name": "DeferredDeliveryTime"
},
"0x0010": {
"data_type": "0x0040",
"name": "DeliverTime"
},
"0x0015": {
"data_type": "0x0040",
"name": "ExpiryTime"
},
"0x0017": {
"data_type": "0x0003",
"name": "Importance"
},
"0x001A": {
"data_type": "0x001F",
"name": "MessageClass"
},
"0x0023": {
"data_type": "0x000B",
"name": "OriginatorDeliveryReportRequested"
},
"0x0025": {
"data_type": "0x0102",
"name": "ParentKey"
},
"0x0026": {
"data_type": "0x0003",
"name": "Priority"
},
"0x0029": {
"data_type": "0x000B",
"name": "ReadReceiptRequested"
},
"0x002A": {
"data_type": "0x0040",
"name": "ReceiptTime"
},
"0x002B": {
"data_type": "0x000B",
"name": "RecipientReassignmentProhibited"
},
"0x002E": {
"data_type": "0x0003",
"name": "OriginalSensitivity"
},
"0x0030": {
"data_type": "0x0040",
"name": "ReplyTime"
},
"0x0031": {
"data_type": "0x0102",
"name": "ReportTag"
},
"0x0032": {
"data_type": "0x0040",
"name": "ReportTime"
},
"0x0036": {
"data_type": "0x0003",
"name": "Sensitivity"
},
"0x0037": {
"data_type": "0x001F",
"name": "Subject"
},
"0x0039": {
"data_type": "0x0040",
"name": "ClientSubmitTime"
},
"0x003A": {
"data_type": "0x001F",
"name": "ReportName"
},
"0x003B": {
"data_type": "0x0102",
"name": "SentRepresentingSearchKey"
},
"0x003D": {
"data_type": "0x001F",
"name": "SubjectPrefix"
},
"0x003F": {
"data_type": "0x0102",
"name": "ReceivedByEntryId"
},
"0x0040": {
"data_type": "0x001F",
"name": "ReceivedByName"
},
"0x0041": {
"data_type": "0x0102",
"name": "SentRepresentingEntryId"
},
"0x0042": {
"data_type": "0x001F",
"name": "SentRepresentingName"
},
"0x0043": {
"data_type": "0x0102",
"name": "ReceivedRepresentingEntryId"
},
"0x0044": {
"data_type": "0x001F",
"name": "ReceivedRepresentingName"
},
"0x0045": {
"data_type": "0x0102",
"name": "ReportEntryId"
},
"0x0046": {
"data_type": "0x0102",
"name": "ReadReceiptEntryId"
},
"0x0047": {
"data_type": "0x0102",
"name": "MessageSubmissionId"
},
"0x0049": {
"data_type": "0x001F",
"name": "OriginalSubject"
},
"0x004B": {
"data_type": "0x001F",
"name": "OriginalMessageClass"
},
"0x004C": {
"data_type": "0x0102",
"name": "OriginalAuthorEntryId"
},
"0x004D": {
"data_type": "0x001F",
"name": "OriginalAuthorName"
},
"0x004E": {
"data_type": "0x0040",
"name": "OriginalSubmitTime"
},
"0x004F": {
"data_type": "0x0102",
"name": "ReplyRecipientEntries"
},
"0x0050": {
"data_type": "0x001F",
"name": "ReplyRecipientNames"
},
"0x0051": {
"data_type": "0x0102",
"name": "ReceivedBySearchKey"
},
"0x0052": {
"data_type": "0x0102",
"name": "ReceivedRepresentingSearchKey"
},
"0x0053": {
"data_type": "0x0102",
"name": "ReadReceiptSearchKey"
},
"0x0054": {
"data_type": "0x0102",
"name": "ReportSearchKey"
},
"0x0055": {
"data_type": "0x0040",
"name": "OriginalDeliveryTime"
},
"0x0057": {
"data_type": "0x000B",
"name": "MessageToMe"
},
"0x0058": {
"data_type": "0x000B",
"name": "MessageCcMe"
},
"0x0059": {
"data_type": "0x000B",
"name": "MessageRecipientMe"
},
"0x005A": {
"data_type": "0x001F",
"name": "OriginalSenderName"
},
"0x005B": {
"data_type": "0x0102",
"name": "OriginalSenderEntryId"
},
"0x005C": {
"data_type": "0x0102",
"name": "OriginalSenderSearchKey"
},
"0x005D": {
"data_type": "0x001F",
"name": "OriginalSentRepresentingName"
},
"0x005E": {
"data_type": "0x0102",
"name": "OriginalSentRepresentingEntryId"
},
"0x005F": {
"data_type": "0x0102",
"name": "OriginalSentRepresentingSearchKey"
},
"0x0060": {
"data_type": "0x0040",
"name": "StartDate"
},
"0x0061": {
"data_type": "0x0040",
"name": "EndDate"
},
"0x0062": {
"data_type": "0x0003",
"name": "OwnerAppointmentId"
},
"0x0063": {
"data_type": "0x000B",
"name": "ResponseRequested"
},
"0x0064": {
"data_type": "0x001F",
"name": "SentRepresentingAddressType"
},
"0x0065": {
"data_type": "0x001F",
"name": "SentRepresentingEmailAddress"
},
"0x0066": {
"data_type": "0x001F",
"name": "OriginalSenderAddressType"
},
"0x0067": {
"data_type": "0x001F",
"name": "OriginalSenderEmailAddress"
},
"0x0068": {
"data_type": "0x001F",
"name": "OriginalSentRepresentingAddressType"
},
"0x0069": {
"data_type": "0x001F",
"name": "OriginalSentRepresentingEmailAddress"
},
"0x0070": {
"data_type": "0x001F",
"name": "ConversationTopic"
},
"0x0071": {
"data_type": "0x0102",
"name": "ConversationIndex"
},
"0x0072": {
"data_type": "0x001F",
"name": "OriginalDisplayBcc"
},
"0x0073": {
"data_type": "0x001F",
"name": "OriginalDisplayCc"
},
"0x0074": {
"data_type": "0x001F",
"name": "OriginalDisplayTo"
},
"0x0075": {
"data_type": "0x001F",
"name": "ReceivedByAddressType"
},
"0x0076": {
"data_type": "0x001F",
"name": "ReceivedByEmailAddress"
},
"0x0077": {
"data_type": "0x001F",
"name": "ReceivedRepresentingAddressType"
},
"0x0078": {
"data_type": "0x001F",
"name": "ReceivedRepresentingEmailAddress"
},
"0x007D": {
"data_type": "0x001F",
"name": "TransportMessageHeaders"
},
"0x007F": {
"data_type": "0x0102",
"name": "TnefCorrelationKey"
},
"0x0080": {
"data_type": "0x001F",
"name": "ReportDisposition"
},
"0x0081": {
"data_type": "0x001F",
"name": "ReportDispositionMode"
},
"0x0807": {
"data_type": "0x0003",
"name": "AddressBookRoomCapacity"
},
"0x0809": {
"data_type": "0x001F",
"name": "AddressBookRoomDescription"
},
"0x0C04": {
"data_type": "0x0003",
"name": "NonDeliveryReportReasonCode"
},
"0x0C05": {
"data_type": "0x0003",
"name": "NonDeliveryReportDiagCode"
},
"0x0C06": {
"data_type": "0x000B",
"name": "NonReceiptNotificationRequested"
},
"0x0C08": {
"data_type": "0x000B",
"name": "OriginatorNonDeliveryReportRequested"
},
"0x0C15": {
"data_type": "0x0003",
"name": "RecipientType"
},
"0x0C17": {
"data_type": "0x000B",
"name": "ReplyRequested"
},
"0x0C19": {
"data_type": "0x0102",
"name": "SenderEntryId"
},
"0x0C1A": {
"data_type": "0x001F",
"name": "SenderName"
},
"0x0C1B": {
"data_type": "0x001F",
"name": "SupplementaryInfo"
},
"0x0C1D": {
"data_type": "0x0102",
"name": "SenderSearchKey"
},
"0x0C1E": {
"data_type": "0x001F",
"name": "SenderAddressType"
},
"0x0C1F": {
"data_type": "0x001F",
"name": "SenderEmailAddress"
},
"0x0C21": {
"data_type": "0x001F",
"name": "RemoteMessageTransferAgent"
},
"0x0E01": {
"data_type": "0x000B",
"name": "DeleteAfterSubmit"
},
"0x0E02": {
"data_type": "0x001F",
"name": "DisplayBcc"
},
"0x0E03": {
"data_type": "0x001F",
"name": "DisplayCc"
},
"0x0E04": {
"data_type": "0x001F",
"name": "DisplayTo"
},
"0x0E06": {
"data_type": "0x0040",
"name": "MessageDeliveryTime"
},
"0x0E07": {
"data_type": "0x0003",
"name": "MessageFlags"
},
"0x0E08": {
"data_type": "0x0014",
"name": "MessageSizeExtended"
},
"0x0E09": {
"data_type": "0x0102",
"name": "ParentEntryId"
},
"0x0E0F": {
"data_type": "0x000B",
"name": "Responsibility"
},
"0x0E12": {
"data_type": "0x000D",
"name": "MessageRecipients"
},
"0x0E13": {
"data_type": "0x000D",
"name": "MessageAttachments"
},
"0x0E17": {
"data_type": "0x0003",
"name": "MessageStatus"
},
"0x0E1B": {
"data_type": "0x000B",
"name": "HasAttachments"
},
"0x0E1D": {
"data_type": "0x001F",
"name": "NormalizedSubject"
},
"0x0E1F": {
"data_type": "0x000B",
"name": "RtfInSync"
},
"0x0E20": {
"data_type": "0x0003",
"name": "AttachSize"
},
"0x0E21": {
"data_type": "0x0003",
"name": "AttachNumber"
},
"0x0E28": {
"data_type": "0x001F",
"name": "PrimarySendAccount"
},
"0x0E29": {
"data_type": "0x001F",
"name": "NextSendAcct"
},
"0x0E2B": {
"data_type": "0x0003",
"name": "ToDoItemFlags"
},
"0x0E2C": {
"data_type": "0x0102",
"name": "SwappedToDoStore"
},
"0x0E2D": {
"data_type": "0x0102",
"name": "SwappedToDoData"
},
"0x0E69": {
"data_type": "0x000B",
"name": "Read"
},
"0x0E6A": {
"data_type": "0x001F",
"name": "SecurityDescriptorAsXml"
},
"0x0E79": {
"data_type": "0x0003",
"name": "TrustSender"
},
"0x0E84": {
"data_type": "0x0102",
"name": "ExchangeNTSecurityDescriptor"
},
"0x0E99": {
"data_type": "0x0102",
"name": "ExtendedRuleMessageActions"
},
"0x0E9A": {
"data_type": "0x0102",
"name": "ExtendedRuleMessageCondition"
},
"0x0E9B": {
"data_type": "0x0003",
"name": "ExtendedRuleSizeLimit"
},
"0x0FF4": {
"data_type": "0x0003",
"name": "Access"
},
"0x0FF5": {
"data_type": "0x0003",
"name": "RowType"
},
"0x0FF6": {
"data_type": "0x0102",
"name": "InstanceKey"
},
"0x0FF7": {
"data_type": "0x0003",
"name": "AccessLevel"
},
"0x0FF8": {
"data_type": "0x0102",
"name": "MappingSignature"
},
"0x0FF9": {
"data_type": "0x0102",
"name": "RecordKey"
},
"0x0FFB": {
"data_type": "0x0102",
"name": "StoreEntryId"
},
"0x0FFE": {
"data_type": "0x0003",
"name": "ObjectType"
},
"0x0FFF": {
"data_type": "0x0102",
"name": "EntryId"
},
"0x1000": {
"data_type": "0x001F",
"name": "Body"
},
"0x1001": {
"data_type": "0x001F",
"name": "ReportText"
},
"0x1009": {
"data_type": "0x0102",
"name": "RtfCompressed"
},
"0x1013": {
"data_type": "0x0102",
"name": "Html"
},
"0x1014": {
"data_type": "0x001F",
"name": "BodyContentLocation"
},
"0x1015": {
"data_type": "0x001F",
"name": "BodyContentId"
},
"0x1016": {
"data_type": "0x0003",
"name": "NativeBody"
},
"0x1035": {
"data_type": "0x001F",
"name": "InternetMessageId"
},
"0x1039": {
"data_type": "0x001F",
"name": "InternetReferences"
},
"0x1042": {
"data_type": "0x001F",
"name": "InReplyToId"
},
"0x1043": {
"data_type": "0x001F",
"name": "ListHelp"
},
"0x1044": {
"data_type": "0x001F",
"name": "ListSubscribe"
},
"0x1045": {
"data_type": "0x001F",
"name": "ListUnsubscribe"
},
"0x1046": {
"data_type": "0x001F",
"name": "OriginalMessageId"
},
"0x1080": {
"data_type": "0x0003",
"name": "IconIndex"
},
"0x1081": {
"data_type": "0x0003",
"name": "LastVerbExecuted"
},
"0x1082": {
"data_type": "0x0040",
"name": "LastVerbExecutionTime"
},
"0x1090": {
"data_type": "0x0003",
"name": "FlagStatus"
},
"0x1091": {
"data_type": "0x0040",
"name": "FlagCompleteTime"
},
"0x1095": {
"data_type": "0x0003",
"name": "FollowupIcon"
},
"0x1096": {
"data_type": "0x0003",
"name": "BlockStatus"
},
"0x10C3": {
"data_type": "0x0040",
"name": "ICalendarStartTime"
},
"0x10C4": {
"data_type": "0x0040",
"name": "ICalendarEndTime"
},
"0x10C5": {
"data_type": "0x0040",
"name": "CdoRecurrenceid"
},
"0x10CA": {
"data_type": "0x0040",
"name": "ICalendarReminderNextTime"
},
"0x10F4": {
"data_type": "0x000B",
"name": "AttributeHidden"
},
"0x10F6": {
"data_type": "0x000B",
"name": "AttributeReadOnly"
},
"0x3000": {
"data_type": "0x0003",
"name": "Rowid"
},
"0x3001": {
"data_type": "0x001F",
"name": "DisplayName"
},
"0x3002": {
"data_type": "0x001F",
"name": "AddressType"
},
"0x3003": {
"data_type": "0x001F",
"name": "EmailAddress"
},
"0x3004": {
"data_type": "0x001F",
"name": "Comment"
},
"0x3005": {
"data_type": "0x0003",
"name": "Depth"
},
"0x3007": {
"data_type": "0x0040",
"name": "CreationTime"
},
"0x3008": {
"data_type": "0x0040",
"name": "LastModificationTime"
},
"0x300B": {
"data_type": "0x0102",
"name": "SearchKey"
},
"0x3010": {
"data_type": "0x0102",
"name": "TargetEntryId"
},
"0x3013": {
"data_type": "0x0102",
"name": "ConversationId"
},
"0x3016": {
"data_type": "0x000B",
"name": "ConversationIndexTracking"
},
"0x3018": {
"data_type": "0x0102",
"name": "ArchiveTag"
},
"0x3019": {
"data_type": "0x0102",
"name": "PolicyTag"
},
"0x301A": {
"data_type": "0x0003",
"name": "RetentionPeriod"
},
"0x301B": {
"data_type": "0x0102",
"name": "StartDateEtc"
},
"0x301C": {
"data_type": "0x0040",
"name": "RetentionDate"
},
"0x301D": {
"data_type": "0x0003",
"name": "RetentionFlags"
},
"0x301E": {
"data_type": "0x0003",
"name": "ArchivePeriod"
},
"0x301F": {
"data_type": "0x0040",
"name": "ArchiveDate"
},
"0x340D": {
"data_type": "0x0003",
"name": "StoreSupportMask"
},
"0x340E": {
"data_type": "0x0003",
"name": "StoreState"
},
"0x3600": {
"data_type": "0x0003",
"name": "ContainerFlags"
},
"0x3601": {
"data_type": "0x0003",
"name": "FolderType"
},
"0x3602": {
"data_type": "0x0003",
"name": "ContentCount"
},
"0x3603": {
"data_type": "0x0003",
"name": "ContentUnreadCount"
},
"0x3609": {
"data_type": "0x000B",
"name": "Selectable"
},
"0x360A": {
"data_type": "0x000B",
"name": "Subfolders"
},
"0x360C": {
"data_type": "0x001F",
"name": "Anr"
},
"0x360E": {
"data_type": "0x000D",
"name": "ContainerHierarchy"
},
"0x360F": {
"data_type": "0x000D",
"name": "ContainerContents"
},
"0x3610": {
"data_type": "0x000D",
"name": "FolderAssociatedContents"
},
"0x3613": {
"data_type": "0x001F",
"name": "ContainerClass"
},
"0x36D0": {
"data_type": "0x0102",
"name": "IpmAppointmentEntryId"
},
"0x36D1": {
"data_type": "0x0102",
"name": "IpmContactEntryId"
},
"0x36D2": {
"data_type": "0x0102",
"name": "IpmJournalEntryId"
},
"0x36D3": {
"data_type": "0x0102",
"name": "IpmNoteEntryId"
},
"0x36D4": {
"data_type": "0x0102",
"name": "IpmTaskEntryId"
},
"0x36D5": {
"data_type": "0x0102",
"name": "RemindersOnlineEntryId"
},
"0x36D7": {
"data_type": "0x0102",
"name": "IpmDraftsEntryId"
},
"0x36D8": {
"data_type": "0x1102",
"name": "AdditionalRenEntryIds"
},
"0x36D9": {
"data_type": "0x0102",
"name": "AdditionalRenEntryIdsEx"
},
"0x36DA": {
"data_type": "0x0102",
"name": "ExtendedFolderFlags"
},
"0x36E2": {
"data_type": "0x0003",
"name": "OrdinalMost"
},
"0x36E4": {
"data_type": "0x1102",
"name": "FreeBusyEntryIds"
},
"0x36E5": {
"data_type": "0x001F",
"name": "DefaultPostMessageClass"
},
"0x3701": {
"data_type": "0x000D",
"name": "AttachDataObject"
},
"0x3702": {
"data_type": "0x0102",
"name": "AttachEncoding"
},
"0x3703": {
"data_type": "0x001F",
"name": "AttachExtension"
},
"0x3704": {
"data_type": "0x001F",
"name": "AttachFilename"
},
"0x3705": {
"data_type": "0x0003",
"name": "AttachMethod"
},
"0x3707": {
"data_type": "0x001F",
"name": "AttachLongFilename"
},
"0x3708": {
"data_type": "0x001F",
"name": "AttachPathname"
},
"0x3709": {
"data_type": "0x0102",
"name": "AttachRendering"
},
"0x370A": {
"data_type": "0x0102",
"name": "AttachTag"
},
"0x370B": {
"data_type": "0x0003",
"name": "RenderingPosition"
},
"0x370C": {
"data_type": "0x001F",
"name": "AttachTransportName"
},
"0x370D": {
"data_type": "0x001F",
"name": "AttachLongPathname"
},
"0x370E": {
"data_type": "0x001F",
"name": "AttachMimeTag"
},
"0x370F": {
"data_type": "0x0102",
"name": "AttachAdditionalInformation"
},
"0x3711": {
"data_type": "0x001F",
"name": "AttachContentBase"
},
"0x3712": {
"data_type": "0x001F",
"name": "AttachContentId"
},
"0x3713": {
"data_type": "0x001F",
"name": "AttachContentLocation"
},
"0x3714": {
"data_type": "0x0003",
"name": "AttachFlags"
},
"0x3719": {
"data_type": "0x001F",
"name": "AttachPayloadProviderGuidString"
},
"0x371A": {
"data_type": "0x001F",
"name": "AttachPayloadClass"
},
"0x371B": {
"data_type": "0x001F",
"name": "TextAttachmentCharset"
},
"0x3900": {
"data_type": "0x0003",
"name": "DisplayType"
},
"0x3902": {
"data_type": "0x0102",
"name": "Templateid"
},
"0x3905": {
"data_type": "0x0003",
"name": "DisplayTypeEx"
},
"0x39FE": {
"data_type": "0x001F",
"name": "SmtpAddress"
},
"0x39FF": {
"data_type": "0x001F",
"name": "AddressBookDisplayNamePrintable"
},
"0x3A00": {
"data_type": "0x001F",
"name": "Account"
},
"0x3A02": {
"data_type": "0x001F",
"name": "CallbackTelephoneNumber"
},
"0x3A05": {
"data_type": "0x001F",
"name": "Generation"
},
"0x3A06": {
"data_type": "0x001F",
"name": "GivenName"
},
"0x3A07": {
"data_type": "0x001F",
"name": "GovernmentIdNumber"
},
"0x3A08": {
"data_type": "0x001F",
"name": "BusinessTelephoneNumber"
},
"0x3A09": {
"data_type": "0x001F",
"name": "HomeTelephoneNumber"
},
"0x3A0A": {
"data_type": "0x001F",
"name": "Initials"
},
"0x3A0B": {
"data_type": "0x001F",
"name": "Keyword"
},
"0x3A0C": {
"data_type": "0x001F",
"name": "Language"
},
"0x3A0D": {
"data_type": "0x001F",
"name": "Location"
},
"0x3A0F": {
"data_type": "0x001F",
"name": "MessageHandlingSystemCommonName"
},
"0x3A10": {
"data_type": "0x001F",
"name": "OrganizationalIdNumber"
},
"0x3A11": {
"data_type": "0x001F",
"name": "Surname"
},
"0x3A12": {
"data_type": "0x0102",
"name": "OriginalEntryId"
},
"0x3A15": {
"data_type": "0x001F",
"name": "PostalAddress"
},
"0x3A16": {
"data_type": "0x001F",
"name": "CompanyName"
},
"0x3A17": {
"data_type": "0x001F",
"name": "Title"
},
"0x3A18": {
"data_type": "0x001F",
"name": "DepartmentName"
},
"0x3A19": {
"data_type": "0x001F",
"name": "OfficeLocation"
},
"0x3A1A": {
"data_type": "0x001F",
"name": "PrimaryTelephoneNumber"
},
"0x3A1B": {
"data_type": "0x101F",
"name": "Business2TelephoneNumbers"
},
"0x3A1C": {
"data_type": "0x001F",
"name": "MobileTelephoneNumber"
},
"0x3A1D": {
"data_type": "0x001F",
"name": "RadioTelephoneNumber"
},
"0x3A1E": {
"data_type": "0x001F",
"name": "CarTelephoneNumber"
},
"0x3A1F": {
"data_type": "0x001F",
"name": "OtherTelephoneNumber"
},
"0x3A20": {
"data_type": "0x001F",
"name": "TransmittableDisplayName"
},
"0x3A21": {
"data_type": "0x001F",
"name": "PagerTelephoneNumber"
},
"0x3A22": {
"data_type": "0x0102",
"name": "UserCertificate"
},
"0x3A23": {
"data_type": "0x001F",
"name": "PrimaryFaxNumber"
},
"0x3A24": {
"data_type": "0x001F",
"name": "BusinessFaxNumber"
},
"0x3A25": {
"data_type": "0x001F",
"name": "HomeFaxNumber"
},
"0x3A26": {
"data_type": "0x001F",
"name": "Country"
},
"0x3A27": {
"data_type": "0x001F",
"name": "Locality"
},
"0x3A28": {
"data_type": "0x001F",
"name": "StateOrProvince"
},
"0x3A29": {
"data_type": "0x001F",
"name": "StreetAddress"
},
"0x3A2A": {
"data_type": "0x001F",
"name": "PostalCode"
},
"0x3A2B": {
"data_type": "0x001F",
"name": "PostOfficeBox"
},
"0x3A2C": {
"data_type": "0x001F; PtypMultipleBinary, 0x1102",
"name": "TelexNumber"
},
"0x3A2D": {
"data_type": "0x001F",
"name": "IsdnNumber"
},
"0x3A2E": {
"data_type": "0x001F",
"name": "AssistantTelephoneNumber"
},
"0x3A2F": {
"data_type": "0x101F",
"name": "Home2TelephoneNumbers"
},
"0x3A30": {
"data_type": "0x001F",
"name": "Assistant"
},
"0x3A40": {
"data_type": "0x000B",
"name": "SendRichInfo"
},
"0x3A41": {
"data_type": "0x0040",
"name": "WeddingAnniversary"
},
"0x3A42": {
"data_type": "0x0040",
"name": "Birthday"
},
"0x3A43": {
"data_type": "0x001F",
"name": "Hobbies"
},
"0x3A44": {
"data_type": "0x001F",
"name": "MiddleName"
},
"0x3A45": {
"data_type": "0x001F",
"name": "DisplayNamePrefix"
},
"0x3A46": {
"data_type": "0x001F",
"name": "Profession"
},
"0x3A47": {
"data_type": "0x001F",
"name": "ReferredByName"
},
"0x3A48": {
"data_type": "0x001F",
"name": "SpouseName"
},
"0x3A49": {
"data_type": "0x001F",
"name": "ComputerNetworkName"
},
"0x3A4A": {
"data_type": "0x001F",
"name": "CustomerId"
},
"0x3A4B": {
"data_type": "0x001F",
"name": "TelecommunicationsDeviceForDeafTelephoneNumber"
},
"0x3A4C": {
"data_type": "0x001F",
"name": "FtpSite"
},
"0x3A4D": {
"data_type": "0x0002",
"name": "Gender"
},
"0x3A4E": {
"data_type": "0x001F",
"name": "ManagerName"
},
"0x3A4F": {
"data_type": "0x001F",
"name": "Nickname"
},
"0x3A50": {
"data_type": "0x001F",
"name": "PersonalHomePage"
},
"0x3A51": {
"data_type": "0x001F",
"name": "BusinessHomePage"
},
"0x3A57": {
"data_type": "0x001F",
"name": "CompanyMainTelephoneNumber"
},
"0x3A58": {
"data_type": "0x101F",
"name": "ChildrensNames"
},
"0x3A59": {
"data_type": "0x001F",
"name": "HomeAddressCity"
},
"0x3A5A": {
"data_type": "0x001F",
"name": "HomeAddressCountry"
},
"0x3A5B": {
"data_type": "0x001F",
"name": "HomeAddressPostalCode"
},
"0x3A5C": {
"data_type": "0x001F",
"name": "HomeAddressStateOrProvince"
},
"0x3A5D": {
"data_type": "0x001F",
"name": "HomeAddressStreet"
},
"0x3A5E": {
"data_type": "0x001F",
"name": "HomeAddressPostOfficeBox"
},
"0x3A5F": {
"data_type": "0x001F",
"name": "OtherAddressCity"
},
"0x3A60": {
"data_type": "0x001F",
"name": "OtherAddressCountry"
},
"0x3A61": {
"data_type": "0x001F",
"name": "OtherAddressPostalCode"
},
"0x3A62": {
"data_type": "0x001F",
"name": "OtherAddressStateOrProvince"
},
"0x3A63": {
"data_type": "0x001F",
"name": "OtherAddressStreet"
},
"0x3A64": {
"data_type": "0x001F",
"name": "OtherAddressPostOfficeBox"
},
"0x3A70": {
"data_type": "0x1102",
"name": "UserX509Certificate"
},
"0x3A71": {
"data_type": "0x0003",
"name": "SendInternetEncoding"
},
"0x3F08": {
"data_type": "0x0003",
"name": "InitialDetailsPane"
},
"0x3FDE": {
"data_type": "0x0003",
"name": "InternetCodepage"
},
"0x3FDF": {
"data_type": "0x0003",
"name": "AutoResponseSuppress"
},
"0x3FE0": {
"data_type": "0x0102",
"name": "AccessControlListData"
},
"0x3FE3": {
"data_type": "0x000B",
"name": "DelegatedByRule"
},
"0x3FE7": {
"data_type": "0x0003",
"name": "ResolveMethod"
},
"0x3FEA": {
"data_type": "0x000B",
"name": "HasDeferredActionMessages"
},
"0x3FEB": {
"data_type": "0x0003",
"name": "DeferredSendNumber"
},
"0x3FEC": {
"data_type": "0x0003",
"name": "DeferredSendUnits"
},
"0x3FED": {
"data_type": "0x0003",
"name": "ExpiryNumber"
},
"0x3FEE": {
"data_type": "0x0003",
"name": "ExpiryUnits"
},
"0x3FEF": {
"data_type": "0x0040",
"name": "DeferredSendTime"
},
"0x3FF0": {
"data_type": "0x0102",
"name": "ConflictEntryId"
},
"0x3FF1": {
"data_type": "0x0003",
"name": "MessageLocaleId"
},
"0x3FF8": {
"data_type": "0x001F",
"name": "CreatorName"
},
"0x3FF9": {
"data_type": "0x0102",
"name": "CreatorEntryId"
},
"0x3FFA": {
"data_type": "0x001F",
"name": "LastModifierName"
},
"0x3FFB": {
"data_type": "0x0102",
"name": "LastModifierEntryId"
},
"0x3FFD": {
"data_type": "0x0003",
"name": "MessageCodepage"
},
"0x401A": {
"data_type": "0x0003",
"name": "SentRepresentingFlags"
},
"0x4029": {
"data_type": "0x001F",
"name": "ReadReceiptAddressType"
},
"0x402A": {
"data_type": "0x001F",
"name": "ReadReceiptEmailAddress"
},
"0x402B": {
"data_type": "0x001F",
"name": "ReadReceiptName"
},
"0x4076": {
"data_type": "0x0003",
"name": "ContentFilterSpamConfidenceLevel"
},
"0x4079": {
"data_type": "0x0003",
"name": "SenderIdStatus"
},
"0x4082": {
"data_type": "0x0040",
"name": "HierRev"
},
"0x4083": {
"data_type": "0x001F",
"name": "PurportedSenderDomain"
},
"0x5902": {
"data_type": "0x0003",
"name": "InternetMailOverrideFormat"
},
"0x5909": {
"data_type": "0x0003",
"name": "MessageEditorFormat"
},
"0x5D01": {
"data_type": "0x001F",
"name": "SenderSmtpAddress"
},
"0x5D02": {
"data_type": "0x001F",
"name": "SentRepresentingSmtpAddress"
},
"0x5D05": {
"data_type": "0x001F",
"name": "ReadReceiptSmtpAddress"
},
"0x5D07": {
"data_type": "0x001F",
"name": "ReceivedBySmtpAddress"
},
"0x5D08": {
"data_type": "0x001F",
"name": "ReceivedRepresentingSmtpAddress"
},
"0x5FDF": {
"data_type": "0x0003",
"name": "RecipientOrder"
},
"0x5FE1": {
"data_type": "0x000B",
"name": "RecipientProposed"
},
"0x5FE3": {
"data_type": "0x0040",
"name": "RecipientProposedStartTime"
},
"0x5FE4": {
"data_type": "0x0040",
"name": "RecipientProposedEndTime"
},
"0x5FF6": {
"data_type": "0x001F",
"name": "RecipientDisplayName"
},
"0x5FF7": {
"data_type": "0x0102",
"name": "RecipientEntryId"
},
"0x5FFB": {
"data_type": "0x0040",
"name": "RecipientTrackStatusTime"
},
"0x5FFD": {
"data_type": "0x0003",
"name": "RecipientFlags"
},
"0x5FFF": {
"data_type": "0x0003",
"name": "RecipientTrackStatus"
},
"0x6100": {
"data_type": "0x0003",
"name": "JunkIncludeContacts"
},
"0x6101": {
"data_type": "0x0003",
"name": "JunkThreshold"
},
"0x6102": {
"data_type": "0x0003",
"name": "JunkPermanentlyDelete"
},
"0x6103": {
"data_type": "0x0003",
"name": "JunkAddRecipientsToSafeSendersList"
},
"0x6107": {
"data_type": "0x000B",
"name": "JunkPhishingEnableLinks"
},
"0x64F0": {
"data_type": "0x0102",
"name": "MimeSkeleton"
},
"0x65C2": {
"data_type": "0x0102",
"name": "ReplyTemplateId"
},
"0x65E0": {
"data_type": "0x0102",
"name": "SourceKey"
},
"0x65E1": {
"data_type": "0x0102",
"name": "ParentSourceKey"
},
"0x65E2": {
"data_type": "0x0102",
"name": "ChangeKey"
},
"0x65E3": {
"data_type": "0x0102",
"name": "PredecessorChangeList"
},
"0x65E9": {
"data_type": "0x0003",
"name": "RuleMessageState"
},
"0x65EA": {
"data_type": "0x0003",
"name": "RuleMessageUserFlags"
},
"0x65EB": {
"data_type": "0x001F",
"name": "RuleMessageProvider"
},
"0x65EC": {
"data_type": "0x001F",
"name": "RuleMessageName"
},
"0x65ED": {
"data_type": "0x0003",
"name": "RuleMessageLevel"
},
"0x65EE": {
"data_type": "0x0102",
"name": "RuleMessageProviderData"
},
"0x65F3": {
"data_type": "0x0003",
"name": "RuleMessageSequence"
},
"0x6619": {
"data_type": "0x0102",
"name": "UserEntryId"
},
"0x661B": {
"data_type": "0x0102",
"name": "MailboxOwnerEntryId"
},
"0x661C": {
"data_type": "0x001F",
"name": "MailboxOwnerName"
},
"0x661D": {
"data_type": "0x000B",
"name": "OutOfOfficeState"
},
"0x6622": {
"data_type": "0x0102",
"name": "SchedulePlusFreeBusyEntryId"
},
"0x6638": {
"data_type": "0x0102",
"name": "SerializedReplidGuidMap"
},
"0x6639": {
"data_type": "0x0003",
"name": "Rights"
},
"0x663A": {
"data_type": "0x000B",
"name": "HasRules"
},
"0x663B": {
"data_type": "0x0102",
"name": "AddressBookEntryId"
},
"0x663E": {
"data_type": "0x0003",
"name": "HierarchyChangeNumber"
},
"0x6645": {
"data_type": "0x0102",
"name": "ClientActions"
},
"0x6646": {
"data_type": "0x0102",
"name": "DamOriginalEntryId"
},
"0x6647": {
"data_type": "0x000B",
"name": "DamBackPatched"
},
"0x6648": {
"data_type": "0x0003",
"name": "RuleError"
},
"0x6649": {
"data_type": "0x0003",
"name": "RuleActionType"
},
"0x664A": {
"data_type": "0x000B",
"name": "HasNamedProperties"
},
"0x6650": {
"data_type": "0x0003",
"name": "RuleActionNumber"
},
"0x6651": {
"data_type": "0x0102",
"name": "RuleFolderEntryId"
},
"0x666A": {
"data_type": "0x0003",
"name": "ProhibitReceiveQuota"
},
"0x666C": {
"data_type": "0x000B",
"name": "InConflict"
},
"0x666D": {
"data_type": "0x0003",
"name": "MaximumSubmitMessageSize"
},
"0x666E": {
"data_type": "0x0003",
"name": "ProhibitSendQuota"
},
"0x6671": {
"data_type": "0x0014",
"name": "MemberId"
},
"0x6672": {
"data_type": "0x001F",
"name": "MemberName"
},
"0x6673": {
"data_type": "0x0003",
"name": "MemberRights"
},
"0x6674": {
"data_type": "0x0014",
"name": "RuleId"
},
"0x6675": {
"data_type": "0x0102",
"name": "RuleIds"
},
"0x6676": {
"data_type": "0x0003",
"name": "RuleSequence"
},
"0x6677": {
"data_type": "0x0003",
"name": "RuleState"
},
"0x6678": {
"data_type": "0x0003",
"name": "RuleUserFlags"
},
"0x6679": {
"data_type": "0x00FD",
"name": "RuleCondition"
},
"0x6680": {
"data_type": "0x00FE",
"name": "RuleActions"
},
"0x6681": {
"data_type": "0x001F",
"name": "RuleProvider"
},
"0x6682": {
"data_type": "0x001F",
"name": "RuleName"
},
"0x6683": {
"data_type": "0x0003",
"name": "RuleLevel"
},
"0x6684": {
"data_type": "0x0102",
"name": "RuleProviderData"
},
"0x668F": {
"data_type": "0x0040",
"name": "DeletedOn"
},
"0x66A1": {
"data_type": "0x0003",
"name": "LocaleId"
},
"0x66A8": {
"data_type": "0x0003",
"name": "FolderFlags"
},
"0x66C3": {
"data_type": "0x0003",
"name": "CodePageId"
},
"0x6704": {
"data_type": "0x000D",
"name": "AddressBookManageDistributionList"
},
"0x6705": {
"data_type": "0x0003",
"name": "SortLocaleId"
},
"0x6709": {
"data_type": "0x0040",
"name": "LocalCommitTime"
},
"0x670A": {
"data_type": "0x0040",
"name": "LocalCommitTimeMax"
},
"0x670B": {
"data_type": "0x0003",
"name": "DeletedCountTotal"
},
"0x670E": {
"data_type": "0x001F",
"name": "FlatUrlName"
},
"0x6740": {
"data_type": "0x00FB",
"name": "SentMailSvrEID"
},
"0x6741": {
"data_type": "0x00FB",
"name": "DeferredActionMessageOriginalEntryId"
},
"0x6748": {
"data_type": "0x0014",
"name": "FolderId"
},
"0x6749": {
"data_type": "0x0014",
"name": "ParentFolderId"
},
"0x674A": {
"data_type": "0x0014",
"name": "Mid"
},
"0x674D": {
"data_type": "0x0014",
"name": "InstID"
},
"0x674E": {
"data_type": "0x0003",
"name": "InstanceNum"
},
"0x674F": {
"data_type": "0x0014",
"name": "AddressBookMessageId"
},
"0x67A4": {
"data_type": "0x0014",
"name": "ChangeNumber"
},
"0x67AA": {
"data_type": "0x000B",
"name": "Associated"
},
"0x6800": {
"data_type": "0x001F",
"name": "OfflineAddressBookName"
},
"0x6801": {
"data_type": "0x0003",
"name": "VoiceMessageDuration"
},
"0x6802": {
"data_type": "0x001F",
"name": "SenderTelephoneNumber"
},
"0x6803": {
"data_type": "0x001F",
"name": "VoiceMessageSenderName"
},
"0x6804": {
"data_type": "0x001E",
"name": "OfflineAddressBookDistinguishedName"
},
"0x6805": {
"data_type": "0x001F",
"name": "VoiceMessageAttachmentOrder"
},
"0x6806": {
"data_type": "0x001F",
"name": "CallId"
},
"0x6820": {
"data_type": "0x001F",
"name": "ReportingMessageTransferAgent"
},
"0x6834": {
"data_type": "0x0003",
"name": "SearchFolderLastUsed"
},
"0x683A": {
"data_type": "0x0003",
"name": "SearchFolderExpiration"
},
"0x6841": {
"data_type": "0x0003",
"name": "SearchFolderTemplateId"
},
"0x6842": {
"data_type": "0x0102",
"name": "WlinkGroupHeaderID"
},
"0x6843": {
"data_type": "0x000B",
"name": "ScheduleInfoDontMailDelegates"
},
"0x6844": {
"data_type": "0x0102",
"name": "SearchFolderRecreateInfo"
},
"0x6845": {
"data_type": "0x0102",
"name": "SearchFolderDefinition"
},
"0x6846": {
"data_type": "0x0003",
"name": "SearchFolderStorageType"
},
"0x6847": {
"data_type": "0x0003",
"name": "WlinkSaveStamp"
},
"0x6848": {
"data_type": "0x0003",
"name": "SearchFolderEfpFlags"
},
"0x6849": {
"data_type": "0x0003",
"name": "WlinkType"
},
"0x684A": {
"data_type": "0x0003",
"name": "WlinkFlags"
},
"0x684B": {
"data_type": "0x0102",
"name": "WlinkOrdinal"
},
"0x684C": {
"data_type": "0x0102",
"name": "WlinkEntryId"
},
"0x684D": {
"data_type": "0x0102",
"name": "WlinkRecordKey"
},
"0x684E": {
"data_type": "0x0102",
"name": "WlinkStoreEntryId"
},
"0x684F": {
"data_type": "0x0102",
"name": "WlinkFolderType"
},
"0x6850": {
"data_type": "0x0102",
"name": "WlinkGroupClsid"
},
"0x6851": {
"data_type": "0x001F",
"name": "WlinkGroupName"
},
"0x6852": {
"data_type": "0x0003",
"name": "WlinkSection"
},
"0x6853": {
"data_type": "0x0003",
"name": "WlinkCalendarColor"
},
"0x6854": {
"data_type": "0x0102",
"name": "WlinkAddressBookEID"
},
"0x6855": {
"data_type": "0x1003",
"name": "ScheduleInfoMonthsAway"
},
"0x6856": {
"data_type": "0x1102",
"name": "ScheduleInfoFreeBusyAway"
},
"0x6868": {
"data_type": "0x0040",
"name": "FreeBusyRangeTimestamp"
},
"0x6869": {
"data_type": "0x0003",
"name": "FreeBusyCountMonths"
},
"0x686A": {
"data_type": "0x0102",
"name": "ScheduleInfoAppointmentTombstone"
},
"0x686B": {
"data_type": "0x1003",
"name": "DelegateFlags"
},
"0x686C": {
"data_type": "0x0102",
"name": "ScheduleInfoFreeBusy"
},
"0x686D": {
"data_type": "0x000B",
"name": "ScheduleInfoAutoAcceptAppointments"
},
"0x686E": {
"data_type": "0x000B",
"name": "ScheduleInfoDisallowRecurringAppts"
},
"0x686F": {
"data_type": "0x000B",
"name": "ScheduleInfoDisallowOverlappingAppts"
},
"0x6890": {
"data_type": "0x0102",
"name": "WlinkClientID"
},
"0x6891": {
"data_type": "0x0102",
"name": "WlinkAddressBookStoreEID"
},
"0x6892": {
"data_type": "0x0003",
"name": "WlinkROGroupType"
},
"0x7001": {
"data_type": "0x0102",
"name": "ViewDescriptorBinary"
},
"0x7002": {
"data_type": "0x001F",
"name": "ViewDescriptorStrings"
},
"0x7006": {
"data_type": "0x001F",
"name": "ViewDescriptorName"
},
"0x7007": {
"data_type": "0x0003",
"name": "ViewDescriptorVersion"
},
"0x7C06": {
"data_type": "0x0003",
"name": "RoamingDatatypes"
},
"0x7C07": {
"data_type": "0x0102",
"name": "RoamingDictionary"
},
"0x7C08": {
"data_type": "0x0102",
"name": "RoamingXmlStream"
},
"0x7C24": {
"data_type": "0x000B",
"name": "OscSyncEnabled"
},
"0x7D01": {
"data_type": "0x000B",
"name": "Processed"
},
"0x7FF9": {
"data_type": "0x0040",
"name": "ExceptionReplaceTime"
},
"0x7FFA": {
"data_type": "0x0003",
"name": "AttachmentLinkId"
},
"0x7FFB": {
"data_type": "0x0040",
"name": "ExceptionStartTime"
},
"0x7FFC": {
"data_type": "0x0040",
"name": "ExceptionEndTime"
},
"0x7FFD": {
"data_type": "0x0003",
"name": "AttachmentFlags"
},
"0x7FFE": {
"data_type": "0x000B",
"name": "AttachmentHidden"
},
"0x7FFF": {
"data_type": "0x000B",
"name": "AttachmentContactPhoto"
},
"0x8004": {
"data_type": "0x001F",
"name": "AddressBookFolderPathname"
},
"0x8005": {
"data_type": "0x001F",
"name": "AddressBookManagerDistinguishedName"
},
"0x8006": {
"data_type": "0x001E",
"name": "AddressBookHomeMessageDatabase"
},
"0x8008": {
"data_type": "0x001E",
"name": "AddressBookIsMemberOfDistributionList"
},
"0x8009": {
"data_type": "0x000D",
"name": "AddressBookMember"
},
"0x800C": {
"data_type": "0x000D",
"name": "AddressBookOwner"
},
"0x800E": {
"data_type": "0x000D",
"name": "AddressBookReports"
},
"0x800F": {
"data_type": "0x101F",
"name": "AddressBookProxyAddresses"
},
"0x8011": {
"data_type": "0x001F",
"name": "AddressBookTargetAddress"
},
"0x8015": {
"data_type": "0x000D",
"name": "AddressBookPublicDelegates"
},
"0x8024": {
"data_type": "0x000D",
"name": "AddressBookOwnerBackLink"
},
"0x802D": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute1"
},
"0x802E": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute2"
},
"0x802F": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute3"
},
"0x8030": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute4"
},
"0x8031": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute5"
},
"0x8032": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute6"
},
"0x8033": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute7"
},
"0x8034": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute8"
},
"0x8035": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute9"
},
"0x8036": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute10"
},
"0x803C": {
"data_type": "0x001F",
"name": "AddressBookObjectDistinguishedName"
},
"0x806A": {
"data_type": "0x0003",
"name": "AddressBookDeliveryContentLength"
},
"0x8073": {
"data_type": "0x000D",
"name": "AddressBookDistributionListMemberSubmitAccepted"
},
"0x8170": {
"data_type": "0x101F",
"name": "AddressBookNetworkAddress"
},
"0x8C57": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute11"
},
"0x8C58": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute12"
},
"0x8C59": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute13"
},
"0x8C60": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute14"
},
"0x8C61": {
"data_type": "0x001F",
"name": "AddressBookExtensionAttribute15"
},
"0x8C6A": {
"data_type": "0x1102",
"name": "AddressBookX509Certificate"
},
"0x8C6D": {
"data_type": "0x0102",
"name": "AddressBookObjectGuid"
},
"0x8C8E": {
"data_type": "0x001F",
"name": "AddressBookPhoneticGivenName"
},
"0x8C8F": {
"data_type": "0x001F",
"name": "AddressBookPhoneticSurname"
},
"0x8C90": {
"data_type": "0x001F",
"name": "AddressBookPhoneticDepartmentName"
},
"0x8C91": {
"data_type": "0x001F",
"name": "AddressBookPhoneticCompanyName"
},
"0x8C92": {
"data_type": "0x001F",
"name": "AddressBookPhoneticDisplayName"
},
"0x8C93": {
"data_type": "0x0003",
"name": "AddressBookDisplayTypeExtended"
},
"0x8C94": {
"data_type": "0x000D",
"name": "AddressBookHierarchicalShowInDepartments"
},
"0x8C96": {
"data_type": "0x101F",
"name": "AddressBookRoomContainers"
},
"0x8C97": {
"data_type": "0x000D",
"name": "AddressBookHierarchicalDepartmentMembers"
},
"0x8C98": {
"data_type": "0x001E",
"name": "AddressBookHierarchicalRootDepartment"
},
"0x8C99": {
"data_type": "0x000D",
"name": "AddressBookHierarchicalParentDepartment"
},
"0x8C9A": {
"data_type": "0x000D",
"name": "AddressBookHierarchicalChildDepartments"
},
"0x8C9E": {
"data_type": "0x0102",
"name": "ThumbnailPhoto"
},
"0x8CA0": {
"data_type": "0x0003",
"name": "AddressBookSeniorityIndex"
},
"0x8CA8": {
"data_type": "0x001F",
"name": "AddressBookOrganizationalUnitRootDistinguishedName"
},
"0x8CAC": {
"data_type": "0x101F",
"name": "AddressBookSenderHintTranslations"
},
"0x8CB5": {
"data_type": "0x000B",
"name": "AddressBookModerationEnabled"
},
"0x8CC2": {
"data_type": "0x0102",
"name": "SpokenName"
},
"0x8CD8": {
"data_type": "0x000D",
"name": "AddressBookAuthorizedSenders"
},
"0x8CD9": {
"data_type": "0x000D",
"name": "AddressBookUnauthorizedSenders"
},
"0x8CDA": {
"data_type": "0x000D",
"name": "AddressBookDistributionListMemberSubmitRejected"
},
"0x8CDB": {
"data_type": "0x000D",
"name": "AddressBookDistributionListRejectMessagesFromDLMembers"
},
"0x8CDD": {
"data_type": "0x000B",
"name": "AddressBookHierarchicalIsHierarchicalGroup"
},
"0x8CE2": {
"data_type": "0x0003",
"name": "AddressBookDistributionListMemberCount"
},
"0x8CE3": {
"data_type": "0x0003",
"name": "AddressBookDistributionListExternalMemberCount"
},
"0xFFFB": {
"data_type": "0x000B",
"name": "AddressBookIsMaster"
},
"0xFFFC": {
"data_type": "0x0102",
"name": "AddressBookParentEntryId"
},
"0xFFFD": {
"data_type": "0x0003",
"name": "AddressBookContainerId"
}
}
''' HELPER FUNCTION '''
def recursive_convert_to_unicode(replace_to_utf):
"""Converts object into UTF-8 characters
ignores errors
Args:
replace_to_utf (object): any object
Returns:
object converted to UTF-8
"""
try:
if isinstance(replace_to_utf, dict):
return {recursive_convert_to_unicode(k): recursive_convert_to_unicode(v) for k, v in replace_to_utf.items()}
if isinstance(replace_to_utf, list):
return [recursive_convert_to_unicode(i) for i in replace_to_utf if i]
if isinstance(replace_to_utf, str):
return unicode(replace_to_utf, encoding='utf-8', errors='ignore')
if not replace_to_utf:
return replace_to_utf
return replace_to_utf
except TypeError:
return replace_to_utf
TOP_LEVEL_HEADER_SIZE = 32
RECIPIENT_HEADER_SIZE = 8
ATTACHMENT_HEADER_SIZE = 8
EMBEDDED_MSG_HEADER_SIZE = 24
CONTROL_CHARS = re.compile(r'[\n\r\t]')
MIME_ENCODED_WORD = re.compile(r'(.*)=\?(.+)\?([B|Q])\?(.+)\?=(.*)') # guardrails-disable-line
USER_ENCODING = demisto.args().get('forced_encoding', '')
DEFAULT_ENCODING = demisto.args().get('default_encoding', '')
class Message(object):
"""
Class to store Message properties
"""
def __init__(self, directory_entries, parent_directory_path=None):
if parent_directory_path is None:
parent_directory_path = []
self._streams = self._process_directory_entries(directory_entries)
self.embedded_messages = [] # type: list
self._data_model = DataModel()
self._parent_directory_path = parent_directory_path
self._nested_attachments_depth = 0
self.properties = self._get_properties()
self.attachments = self._get_attachments()
self.recipients = self._get_recipients()
self._set_properties()
self._set_attachments()
self._set_recipients()
def _get_attachments_names(self):
names = []
for attachment in self.attachments:
names.append(attachment.DisplayName or attachment.Filename)
return names
def get_all_attachments(self):
attachments = self.attachments
for embedded_message in self.embedded_messages:
attachments.extend(embedded_message.get_all_attachments())
return attachments
def as_dict(self, max_depth):
if max_depth == 0:
return None
def join(arr):
if isinstance(arr, list):
arr = [item for item in arr if item is not None]
return ",".join(arr)
return ""
cc = None
if self.cc is not None:
cc = join([extract_address(cc) for cc in self.cc]) # noqa: F812
bcc = None
if self.bcc is not None:
bcc = join([extract_address(bcc) for bcc in self.bcc]) # noqa
recipients = None
if self.to is not None:
recipients = join([extract_address(recipient.EmailAddress) for recipient in self.recipients]) # noqa
sender = None
if self.sender is not None:
sender = join([extract_address(sender) for sender in self.sender]) # noqa
html = self.html
if not html:
html = self.body
message_dict = {
'Attachments': join(self._get_attachments_names()),
'CC': cc,
'BCC': bcc,
'To': recipients,
'From': sender,
'Subject': self.subject,
'Text': self.body,
'HTML': html,
'Headers': str(self.header) if self.header is not None else None,
'HeadersMap': self.header_dict,
'Depth': MAX_DEPTH_CONST - max_depth
}
return message_dict
def get_attached_emails_hierarchy(self, max_depth):
if max_depth == 0:
return []
attached_emails = []
for embedded_message in self.embedded_messages:
attached_emails.append(embedded_message.as_dict(max_depth))
attached_emails.extend(embedded_message.get_attached_emails_hierarchy(max_depth - 1))
return attached_emails
def _set_property_stream_info(self, ole_file, header_size):
property_dir_entry = ole_file.openstream('__properties_version1.0')
version_stream_data = property_dir_entry.read()
if not version_stream_data:
raise Exception("Invalid MSG file provided, 'properties_version1.0' stream data is empty.")
if version_stream_data:
if header_size >= EMBEDDED_MSG_HEADER_SIZE:
properties_metadata = unpack('8sIIII', version_stream_data[:24])
if not properties_metadata or not len(properties_metadata) >= 5:
raise Exception("'properties_version1.0' stream data is corrupted.")
self.next_recipient_id = properties_metadata[1]
self.next_attachment_id = properties_metadata[2]
self.recipient_count = properties_metadata[3]
self.attachment_count = properties_metadata[4]
if (len(version_stream_data) - header_size) % 16 != 0:
raise Exception('Property Stream size less header is not exactly divisible by 16')
self.property_entries_count = (len(version_stream_data) - header_size) / 16
@staticmethod
def _process_directory_entries(directory_entries):
streams = {
"properties": {},
"recipients": {},
"attachments": {}
} # type: dict
for name, stream in directory_entries.iteritems():
# collect properties
if "__substg1.0_" in name:
streams["properties"][name] = stream
# collect attachments
elif "__attach_" in name:
streams["attachments"][name] = stream.kids
# collect recipients
elif "__recip_" in name:
streams["recipients"][name] = stream.kids
# unknown stream name
else:
continue
return streams
def _get_properties(self):
directory_entries = self._streams.get("properties")
directory_name_filter = "__substg1.0_"
property_entries = {}
for directory_name, directory_entry in directory_entries.iteritems():
if directory_name_filter not in directory_name:
continue
if not directory_entry:
continue
if isinstance(directory_entry, list):
directory_values = {} # type: dict
for property_entry in directory_entry:
property_data = self._get_property_data(directory_name, property_entry, is_list=True)
if property_data:
directory_values.update(property_data)
property_entries[directory_name] = directory_values
else:
property_data = self._get_property_data(directory_name, directory_entry)
if property_data:
property_entries.update(property_data)
return property_entries
def _get_recipients(self):
directory_entries = self._streams.get("recipients")
directory_name_filter = "__recip_version1.0_"
recipient_entries = {}
for directory_name, directory_entry in directory_entries.iteritems():
if directory_name_filter not in directory_name:
continue
if not directory_entry:
continue
if isinstance(directory_entry, list):
directory_values = {} # type: dict
for property_entry in directory_entry:
property_data = self._get_property_data(directory_name, property_entry, is_list=True)
if property_data:
directory_values.update(property_data)
recipient_address = directory_values.get(
'EmailAddress', directory_values.get('SmtpAddress', directory_name)
)
recipient_entries[recipient_address] = directory_values
else:
property_data = self._get_property_data(directory_name, directory_entry)
if property_data:
recipient_entries.update(property_data)
return recipient_entries
def _get_attachments(self):
directory_entries = self._streams.get("attachments")
directory_name_filter = "__attach_version1.0_"
attachment_entries = {}
for directory_name, directory_entry in directory_entries.iteritems():
if directory_name_filter not in directory_name:
continue
if not directory_entry:
continue
if isinstance(directory_entry, list):
directory_values = {}
for property_entry in directory_entry:
kids = property_entry.kids
if kids:
embedded_message = Message(
property_entry.kids_dict,
self._parent_directory_path + [directory_name, property_entry.name]
)
directory_values["EmbeddedMessage"] = {
"properties": embedded_message.properties,
"recipients": embedded_message.recipients,
"attachments": embedded_message.attachments
}
self.embedded_messages.append(embedded_message)
property_data = self._get_property_data(directory_name, property_entry, is_list=True)
if property_data:
directory_values.update(property_data)
attachment_entries[directory_name] = directory_values
else:
property_data = self._get_property_data(directory_name, directory_entry)
if property_data:
attachment_entries.update(property_data)
return attachment_entries
def _get_property_data(self, directory_name, directory_entry, is_list=False):
directory_entry_name = directory_entry.name
if is_list:
stream_name = [directory_name, directory_entry_name]
else:
stream_name = [directory_entry_name]
if self._parent_directory_path:
stream_name = self._parent_directory_path + stream_name
ole_file = directory_entry.olefile
property_details = self._get_canonical_property_name(directory_entry_name)
if not property_details:
return None
property_name = property_details.get("name")
property_type = property_details.get("data_type")
if not property_type:
demisto.info('could not parse property type, skipping property "{}"'.format(property_details))
return None
try:
raw_content = ole_file.openstream(stream_name).read()
except IOError:
raw_content = ''
if not raw_content:
demisto.debug('Could not read raw content from stream "{}", '
'skipping property "{}"'.format(stream_name, property_details))
return None
property_value = self._data_model.get_value(raw_content, data_type=property_type)
if property_value:
property_detail = {property_name: property_value}
else:
property_detail = None # type: ignore[assignment]
return property_detail
@staticmethod
def _get_canonical_property_name(dir_entry_name):
if not dir_entry_name:
return None
if "__substg1.0_" in dir_entry_name:
name = dir_entry_name.replace("__substg1.0_", "")
prop_name_id = "0x" + name[0:4]
prop_details = PROPS_ID_MAP.get(prop_name_id)
return prop_details
return None
def _set_properties(self):
property_values = self.properties
# setting generally required properties to easily access using MsOxMessage instance.
self.subject = property_values.get("Subject")
header = property_values.get("TransportMessageHeaders")
self.header = parse_email_headers(header, True)
self.header_dict = parse_email_headers(header) or {}
self.created_date = property_values.get("CreationTime")
self.received_date = property_values.get("ReceiptTime")
sent_date = property_values.get("DeliverTime")
if not sent_date:
sent_date = self.header_dict.get("Date")
self.sent_date = sent_date
sender_address = self.header_dict.get("From")
if not sender_address:
sender_address = property_values.get("SenderRepresentingSmtpAddress")
self.sender = sender_address
reply_to_address = self.header_dict.get("Reply-To")
if not reply_to_address:
reply_to_address = property_values.get("ReplyRecipientNames")
self.reply_to = reply_to_address
self.message_id = property_values.get("InternetMessageId")
to_address = self.header_dict.get("To")
if not to_address:
to_address = property_values.get("ReceivedRepresentingSmtpAddress")
if not to_address:
to_address = property_values.get("DisplayTo")
self.to = to_address
to_smpt_address = property_values.get("ReceivedRepresentingSmtpAddress")
if not to_smpt_address:
to_smpt_address = [value for key, value in self.recipients.iteritems()]
self.to_address = to_smpt_address
cc_address = self.header_dict.get("CC")
# if cc_address:
# cc_address = [CONTROL_CHARS.sub(" ", cc_add) for cc_add in cc_address.split(",")]
self.cc = cc_address
bcc_address = self.header_dict.get("BCC")
self.bcc = bcc_address
# prefer HTMl over plain text
self.html = property_values.get("Html")
self.body = property_values.get("Body")
if not self.body and "RtfCompressed" in property_values:
try:
import compressed_rtf
except ImportError:
compressed_rtf = None
if compressed_rtf:
compressed_rtf_body = property_values['RtfCompressed']
self.body = compressed_rtf.decompress(compressed_rtf_body)
def _set_recipients(self):
recipients = self.recipients
self.recipients = []
for recipient_name, recipient in recipients.items():
if self.to and recipient_name in self.to:
recipient["RecipientType"] = "TO"
if self.cc and recipient_name in self.cc:
recipient["RecipientType"] = "CC"
if self.bcc and recipient_name in self.bcc:
recipient["RecipientType"] = "BCC"
if self.reply_to and recipient_name in self.reply_to:
recipient["RecipientType"] = "ReplyTo"
self.recipients.append(Recipient(recipient))
def _set_attachments(self):
attachments = self.attachments
self.attachments = [Attachment(attach) for attach in attachments.values()]
def __repr__(self):
return u'Message [%s]' % self.properties.get('InternetMessageId', self.properties.get("Subject"))
class Recipient(object):
"""
class to store recipient attributes
"""
def __init__(self, recipients_properties):
self.AddressType = recipients_properties.get("AddressType")
self.Account = recipients_properties.get("Account")
self.EmailAddress = recipients_properties.get("SmtpAddress")
self.DisplayName = recipients_properties.get("DisplayName")
self.ObjectType = recipients_properties.get("ObjectType")
self.RecipientType = recipients_properties.get("RecipientType")
def __repr__(self):
return '%s (%s)' % (self.DisplayName, self.EmailAddress)
class Attachment(object):
"""
class to store attachment attributes
"""
def __init__(self, attachment_properties):
self.DisplayName = attachment_properties.get("DisplayName")
self.AttachEncoding = attachment_properties.get("AttachEncoding")
self.AttachContentId = attachment_properties.get("AttachContentId")
self.AttachMethod = attachment_properties.get("AttachMethod")
self.AttachmentSize = format_size(attachment_properties.get("AttachmentSize"))
self.AttachFilename = attachment_properties.get("AttachFilename")
self.AttachLongFilename = attachment_properties.get("AttachLongFilename")
if self.AttachLongFilename:
self.Filename = self.AttachLongFilename
else:
self.Filename = self.AttachFilename
if self.Filename:
self.Filename = os.path.basename(self.Filename)
else:
self.Filename = '[NoFilename_Method%s]' % self.AttachMethod
self.data = attachment_properties.get("AttachDataObject")
self.AttachMimeTag = attachment_properties.get("AttachMimeTag", "application/octet-stream")
self.AttachExtension = attachment_properties.get("AttachExtension")
def __repr__(self):
return '%s (%s / %s)' % (self.Filename, self.AttachmentSize, len(self.data or []))
class MsOxMessage(object):
"""
Base class for Microsoft Message Object
"""
def __init__(self, msg_file_path):
self.msg_file_path = msg_file_path
self.include_attachment_data = False
if not self.is_valid_msg_file():
raise Exception("Invalid file provided, please provide valid Microsoft Outlook MSG file.")
ole_file = None
try:
ole_file = OleFileIO(msg_file_path)
# process directory entries
ole_root = ole_file.root
kids_dict = ole_root.kids_dict
self._message = Message(kids_dict)
finally:
if ole_file is not None:
ole_file.close()
def as_dict(self, max_depth):
return self._message.as_dict(max_depth)
def get_email_mime_content(self):
email_obj = EmailFormatter(self)
return email_obj.build_email()
def save_email_file(self, file_path):
email_obj = EmailFormatter(self)
email_obj.save_file(file_path)
return True
def get_attached_emails_hierarchy(self, max_depth):
return self._message.get_attached_emails_hierarchy(max_depth)
def is_valid_msg_file(self):
if not os.path.exists(self.msg_file_path):
return False
if not isOleFile(self.msg_file_path):
return False
return True
def get_all_attachments(self):
return self._message.get_all_attachments()
def format_size(num, suffix='B'):
if not num:
return "unknown"
for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
if abs(num) < 1024.0:
return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix)
def parse_email_headers(header, raw=False):
if not header:
return None
headers = email.message_from_string(header)
if raw:
return headers
email_address_headers = { # type: ignore[var-annotated]
"To": [],
"From": [],
"CC": [],
"BCC": [],
"Reply-To": [],
}
for addr in email_address_headers.keys():
for (name, email_address) in email.utils.getaddresses(headers.get_all(addr, [])):
email_address_headers[addr].append("{} <{}>".format(name, email_address))
parsed_headers = dict(headers)
parsed_headers.update(email_address_headers)
return parsed_headers
def get_msg_mail_format(msg_dict):
try:
return msg_dict.get('Headers', 'Content-type:').split('Content-type:')[1].split(';')[0]
except Exception as e:
demisto.debug('Got exception while trying to get msg mail format - {}'.format(str(e)))
return ''
def is_valid_header_to_parse(header):
return len(header) > 0 and not header == ' ' and 'From nobody' not in header
def create_headers_map(msg_dict_headers):
headers = list() # type: list
headers_map = dict() # type: dict
if not msg_dict_headers:
return headers, headers_map
header_key = 'initial key'
header_value = 'initial header'
for header in msg_dict_headers.split('\n'):
if is_valid_header_to_parse(header):
if not header[0] == ' ' and not header[0] == '\t':
if header_value != 'initial header':
header_value = convert_to_unicode(header_value)
headers.append(
{
'name': header_key,
'value': header_value
}
)
if header_key in headers_map:
# in case there is already such header
# then add that header value to value array
if not isinstance(headers_map[header_key], list):
# convert the existing value to array
headers_map[header_key] = [headers_map[header_key]]
# add the new value to the value array
headers_map[header_key].append(header_value)
else:
headers_map[header_key] = header_value
header_words = header.split(' ', 1)
header_key = header_words[0][:-1]
header_value = ' '.join(header_words[1:])
if not header_value == '' and header_value[-1] == ' ':
header_value = header_value[:-1]
else:
header_value += header[:-1] if header[-1:] == ' ' else header
return headers, headers_map
########################################################################################################################
ENCODINGS_TYPES = set(['utf-8', 'iso8859-1'])
REGEX_EMAIL = r"\b[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+[\b,]"
def extract_address(s):
if type(s) not in [str, unicode]:
return s
res = re.findall(REGEX_EMAIL, s)
if res:
return ', '.join(res)
else:
return s
def get_email_address(eml, entry):
"""
This function gets email addresses from an eml object, i.e eml[entry].
Args:
eml : Email object.
entry (str) : entry to look for in the email. i.e ('To', 'CC', 'From')
Returns:
res (str) : string of all required email addresses.
"""
if entry == 'from':
gel_all_values_from_email_by_entry = [str(current_eml_no_newline).replace('\r\n', '').replace('\n', '')
for current_eml_no_newline in eml.get_all(entry, [])]
else:
gel_all_values_from_email_by_entry = eml.get_all(entry, [])
addresses = getaddresses(gel_all_values_from_email_by_entry)
if addresses:
res = [item[1] for item in addresses]
res = ', '.join(res)
return res
return ''
def extract_address_eml(eml, entry):
"""
This function calls get_email_address in order to get required email addresses from email object.
In addition, this function handles an edge case of '\r\n' in eml['from'] (as explained below).
Args:
eml : Email object.
entry (str) : entry to look for in the email. i.e ('To', 'CC', 'From')
Returns:
res (str) : string of all required email addresses.
"""
email_address = get_email_address(eml, entry)
if email_address:
return email_address
else:
return ''
def data_to_md(email_data, email_file_name=None, parent_email_file=None, print_only_headers=False):
if email_data is None:
return 'No data extracted from email'
email_data = recursive_convert_to_unicode(email_data)
email_file_name = recursive_convert_to_unicode(email_file_name)
parent_email_file = recursive_convert_to_unicode(parent_email_file)
md = u"### Results:\n"
if email_file_name:
md = u"### {}\n".format(email_file_name)
if print_only_headers:
return tableToMarkdown("Email Headers: " + email_file_name, email_data.get('HeadersMap'))
if parent_email_file:
md += u"### Containing email: {}\n".format(parent_email_file)
md += u"* {0}:\t{1}\n".format('From', email_data.get('From') or "")
md += u"* {0}:\t{1}\n".format('To', email_data.get('To') or "")
md += u"* {0}:\t{1}\n".format('CC', email_data.get('CC') or "")
md += u"* {0}:\t{1}\n".format('Subject', email_data.get('Subject') or "")
if email_data.get('Text'):
text = email_data['Text'].replace('<', '[').replace('>', ']')
md += u"* {0}:\t{1}\n".format('Body/Text', text or "")
if email_data.get('HTML'):
md += u"* {0}:\t{1}\n".format('Body/HTML', email_data['HTML'] or "")
md += u"* {0}:\t{1}\n".format('Attachments', email_data.get('Attachments') or "")
md += u"\n\n" + tableToMarkdown('HeadersMap', email_data.get('HeadersMap'))
return md
def save_attachments(attachments, root_email_file_name, max_depth):
attached_emls = []
for attachment in attachments:
if attachment.data is not None:
display_name = attachment.DisplayName if attachment.DisplayName else attachment.AttachFilename
display_name = display_name if display_name else ''
demisto.results(fileResult(display_name, attachment.data))
name_lower = display_name.lower()
if max_depth > 0 and (name_lower.endswith(".eml") or name_lower.endswith('.p7m')):
tf = tempfile.NamedTemporaryFile(delete=False)
try:
tf.write(attachment.data)
tf.close()
inner_eml, attached_inner_emails = handle_eml(tf.name, file_name=root_email_file_name,
max_depth=max_depth)
if inner_eml:
return_outputs(
readable_output=data_to_md(inner_eml, attachment.DisplayName, root_email_file_name),
outputs=None)
attached_emls.append(inner_eml)
if attached_inner_emails:
attached_emls.extend(attached_inner_emails)
finally:
os.remove(tf.name)
return attached_emls
def get_utf_string(text, field):
if text is None:
text = ''
try:
utf_string = text.encode('utf-8')
except Exception as ex:
utf_string = text.decode('utf-8', 'ignore').encode('utf-8')
temp = demisto.uniqueFile()
with open(demisto.investigation()['id'] + '_' + temp, 'wb') as f:
f.write(text)
demisto.results({
'Contents': str(ex) + '\n\nOpen HEX viewer to review.',
'ContentsFormat': formats['text'],
'Type': entryTypes['file'],
'File': field,
'FileID': temp
})
return utf_string
def mime_decode(word_mime_encoded):
prefix, charset, encoding, encoded_text, suffix = word_mime_encoded.groups()
if encoding.lower() == 'b':
byte_string = base64.b64decode(encoded_text)
elif encoding.lower() == 'q':
byte_string = quopri.decodestring(encoded_text, header=True)
return prefix + byte_string.decode(charset) + suffix
def convert_to_unicode(s, is_msg_header=True):
global ENCODINGS_TYPES
try:
res = '' # utf encoded result
if is_msg_header: # Mime encoded words used on message headers only
try:
word_mime_encoded = s and MIME_ENCODED_WORD.search(s)
if word_mime_encoded:
word_mime_decoded = mime_decode(word_mime_encoded)
if word_mime_decoded and not MIME_ENCODED_WORD.search(word_mime_decoded):
# ensure decoding was successful
return word_mime_decoded
except Exception as e:
# in case we failed to mine-decode, we continue and try to decode
demisto.debug('Failed decoding mime-encoded string: {}. Will try regular decoding.'.format(str(e)))
for decoded_s, encoding in decode_header(s): # return a list of pairs(decoded, charset)
if encoding:
try:
res += decoded_s.decode(encoding).encode('utf-8')
except UnicodeDecodeError:
demisto.debug('Failed to decode encoded_string')
replace_decoded = decoded_s.decode(encoding, errors='replace').encode('utf-8')
demisto.debug('Decoded string with replace usage {}'.format(replace_decoded))
res += replace_decoded
ENCODINGS_TYPES.add(encoding)
else:
demisto.debug('Could not find the encoding type of the string, decoding by default with utf-8')
res += decoded_s.decode('utf-8', errors='replace').encode('utf-8')
return res.strip()
except Exception:
for file_data in ENCODINGS_TYPES:
try:
s = s.decode(file_data).encode('utf-8').strip()
break
except: # noqa: E722
pass
return s
def handle_msg(file_path, file_name, parse_only_headers=False, max_depth=3):
if max_depth == 0:
return None, []
msg = MsOxMessage(file_path)
if not msg:
raise Exception("Could not parse msg file!")
msg_dict = msg.as_dict(max_depth)
mail_format_type = get_msg_mail_format(msg_dict)
headers, headers_map = create_headers_map(msg_dict.get('Headers'))
email_data = {
'To': msg_dict['To'],
'CC': msg_dict['CC'],
'From': msg_dict['From'],
'Subject': headers_map.get('Subject'),
'HTML': msg_dict['HTML'],
'Text': msg_dict['Text'],
'Headers': headers,
'HeadersMap': headers_map,
'Attachments': msg_dict.get('Attachments'),
'Format': mail_format_type,
'Depth': MAX_DEPTH_CONST - max_depth
}
if parse_only_headers:
return {"HeadersMap": email_data.get("HeadersMap")}, []
attached_emails_emls = save_attachments(msg.get_all_attachments(), file_name, max_depth - 1)
# add eml attached emails
attached_emails_msg = msg.get_attached_emails_hierarchy(max_depth - 1)
for attached_email in attached_emails_msg:
return_outputs(readable_output=data_to_md(attached_email, None, file_name), outputs=None)
return email_data, attached_emails_emls + attached_emails_msg
def unfold(s):
r"""
Remove folding whitespace from a string by converting line breaks (and any
whitespace adjacent to line breaks) to a single space and removing leading
& trailing whitespace.
From: https://github.com/jwodder/headerparser/blob/master/headerparser/types.py#L39
unfold('This is a \n folded string.\n')
'This is a folded string.'
:param string s: a string to unfold
:rtype: string
"""
return re.sub(r'[ \t]*[\r\n][ \t\r\n]*', ' ', s).strip(' ') if s else s
def decode_attachment_payload(message):
"""Decodes a message from Base64, if fails will outputs its str(message)
"""
msg = message.get_payload()
try:
# In some cases the body content is empty and cannot be decoded.
msg_info = base64.b64decode(msg)
except TypeError:
msg_info = str(msg)
return msg_info
def decode_content(mime):
"""
Decode content
"""
charset = mime.get_content_charset()
payload = mime.get_payload(decode=True)
try:
if payload:
if charset:
return payload.decode(charset)
else:
return payload.decode()
else:
return ''
except Exception:
return payload
def save_file(file_name, file_content):
createdFile = fileResult(file_name, file_content)
fileID = createdFile.get('FileID')
attachment_internal_path = demisto.investigation().get('id') + '_' + fileID
demisto.results(createdFile)
return attachment_internal_path
def handle_eml(file_path, b64=False, file_name=None, parse_only_headers=False, max_depth=3, bom=False):
global ENCODINGS_TYPES
if max_depth == 0:
return None, []
with open(file_path, 'rb') as emlFile:
file_data = emlFile.read()
if b64:
file_data = b64decode(file_data)
if bom:
# decode bytes taking into account BOM and re-encode to utf-8
file_data = file_data.decode("utf-8-sig").encode("utf-8")
parser = HeaderParser()
headers = parser.parsestr(file_data)
# headers is a Message object implementing magic methods of set/get item and contains.
# message object 'contains' method transforms its keys to lower-case, hence there is not a difference when
# approaching it with any casing type, for example, 'message-id' or 'Message-ID' or 'Message-id' or
# 'MeSSage_Id' are all searching for the same key in the headers object.
if "message-id" in headers:
message_id_content = headers["message-id"]
del headers["message-id"]
headers["Message-ID"] = message_id_content
header_list = []
headers_map = {} # type: dict
for item in headers.items():
value = unfold(convert_to_unicode(unfold(item[1])))
item_dict = {
"name": item[0],
"value": value
}
# old way to map headers
header_list.append(item_dict)
# new way to map headers - dictionary
if item[0] in headers_map:
# in case there is already such header
# then add that header value to value array
if not isinstance(headers_map[item[0]], list):
# convert the existing value to array
headers_map[item[0]] = [headers_map[item[0]]]
# add the new value to the value array
headers_map[item[0]].append(value)
else:
headers_map[item[0]] = value
eml = message_from_string(file_data)
if not eml:
raise Exception("Could not parse eml file!")
if parse_only_headers:
return {"HeadersMap": headers_map}, []
html = ''
text = ''
attachment_names = []
attachment_content_ids = []
attachment_content_dispositions = []
attachment_internal_path = []
attached_emails = []
parts = [eml]
while parts:
part = parts.pop()
if (part.is_multipart() or part.get_content_type().startswith('multipart')) \
and "attachment" not in part.get("Content-Disposition", ""):
parts += [part_ for part_ in part.get_payload() if isinstance(part_, email.message.Message)]
elif part.get_filename() or "attachment" in part.get("Content-Disposition", ""):
attachment_file_name = convert_to_unicode(part.get_filename())
attachment_content_id = part.get('Content-ID')
attachment_content_disposition = part.get('Content-Disposition')
if attachment_file_name is None and part.get('filename'):
attachment_file_name = os.path.normpath(part.get('filename'))
if os.path.isabs(attachment_file_name):
attachment_file_name = os.path.basename(attachment_file_name)
if "message/rfc822" in part.get("Content-Type", "") \
or ("application/octet-stream" in part.get("Content-Type", "")
and attachment_file_name.endswith(".eml")):
# .eml files
file_content = "" # type: str
base64_encoded = "base64" in part.get("Content-Transfer-Encoding", "")
if isinstance(part.get_payload(), list) and len(part.get_payload()) > 0:
if attachment_file_name is None or attachment_file_name == "" or attachment_file_name == 'None':
# in case there is no filename for the eml
# we will try to use mail subject as file name
# Subject will be in the email headers
attachment_name = part.get_payload()[0].get('Subject', "no_name_mail_attachment")
attachment_file_name = convert_to_unicode(attachment_name) + '.eml'
file_content = part.get_payload()[0].as_string().strip()
if base64_encoded:
try:
file_content = b64decode(file_content)
except TypeError:
pass # In case the file is a string, decode=True for get_payload is not working
elif isinstance(part.get_payload(), basestring):
file_content = part.get_payload(decode=True)
else:
demisto.debug("found eml attachment with Content-Type=message/rfc822 but has no payload")
if file_content:
# save the eml to war room as file entry
attachment_internal_path.append(save_file(attachment_file_name, file_content))
if file_content and max_depth - 1 > 0:
f = tempfile.NamedTemporaryFile(delete=False)
try:
f.write(file_content)
f.close()
inner_eml, inner_attached_emails = handle_eml(file_path=f.name,
file_name=attachment_file_name,
max_depth=max_depth - 1)
attached_emails.append(inner_eml)
attached_emails.extend(inner_attached_emails)
# if we are outter email is a singed attachment it is a wrapper and we don't return the output of
# this inner email as it will be returned as part of the main result
if 'multipart/signed' not in eml.get_content_type() and inner_eml:
return_outputs(readable_output=data_to_md(inner_eml, attachment_file_name, file_name),
outputs=None)
finally:
os.remove(f.name)
if not file_content:
attachment_internal_path.append(None)
attachment_names.append(attachment_file_name)
attachment_content_ids.append(attachment_content_id)
attachment_content_dispositions.append(attachment_content_disposition)
else:
# .msg and other files (png, jpeg)
if part.is_multipart() and max_depth - 1 > 0:
# email is DSN
msgs = part.get_payload() # human-readable section
for i, individual_message in enumerate(msgs):
msg_info = decode_attachment_payload(individual_message)
attached_emails.append(msg_info)
attachment_file_name = individual_message.get_filename()
attachment_content_id = individual_message.get('Content-ID')
attachment_content_disposition = individual_message.get('Content-Disposition')
if attachment_file_name is None:
attachment_file_name = "unknown_file_name{}".format(i)
attachment_internal_path.append(save_file(attachment_file_name, msg_info))
attachment_names.append(attachment_file_name)
attachment_content_ids.append(attachment_content_id)
attachment_content_dispositions.append(attachment_content_disposition)
else:
file_content = part.get_payload(decode=True)
if attachment_file_name.endswith('.p7s') or not file_content:
attachment_internal_path.append(None)
# fileResult will return an error if file_content is None.
if file_content and not attachment_file_name.endswith('.p7s'):
attachment_internal_path.append(save_file(attachment_file_name, file_content))
if attachment_file_name.endswith(".msg") and max_depth - 1 > 0:
f = tempfile.NamedTemporaryFile(delete=False)
try:
f.write(file_content)
f.close()
inner_msg, inner_attached_emails = handle_msg(f.name, attachment_file_name, False,
max_depth - 1)
attached_emails.append(inner_msg)
attached_emails.extend(inner_attached_emails)
# will output the inner email to the UI
return_outputs(
readable_output=data_to_md(inner_msg, attachment_file_name, file_name),
outputs=None)
finally:
os.remove(f.name)
attachment_names.append(attachment_file_name)
attachment_content_ids.append(attachment_content_id)
attachment_content_dispositions.append(attachment_content_disposition)
demisto.setContext('AttachmentName', attachment_file_name)
elif part.get_content_type() == 'text/html':
# This line replaces a new line that starts with `..` to a newline that starts with `.`
# This is because SMTP duplicate dots for lines that start with `.` and get_payload() doesn't format
# this correctly
part._payload = part._payload.replace('=\r\n..', '=\r\n.')
html = get_utf_string(decode_content(part), 'HTML')
elif part.get_content_type() == 'text/plain':
text = get_utf_string(decode_content(part), 'TEXT')
email_data = None
# if we are parsing a signed attachment there can be one of two options:
# 1. it is 'multipart/signed' so it is probably a wrapper and we can ignore the outer "email"
# 2. if it is 'multipart/signed' but has 'to' address so it is actually a real mail.
if 'multipart/signed' not in eml.get_content_type() \
or ('multipart/signed' in eml.get_content_type()
and (extract_address_eml(eml, 'to') or extract_address_eml(eml, 'from') or eml.get('subject'))):
email_data = {
'To': extract_address_eml(eml, 'to'),
'CC': extract_address_eml(eml, 'cc'),
'From': extract_address_eml(eml, 'from'),
'Subject': convert_to_unicode(unfold(eml['Subject'])),
'HTML': convert_to_unicode(html, is_msg_header=False),
'Text': convert_to_unicode(text, is_msg_header=False),
'Headers': header_list,
'HeadersMap': headers_map,
'Attachments': ','.join(attachment_names) if attachment_names else '',
'AttachmentsData': [
{
"Name": attachment_names[i],
"Content-ID": attachment_content_ids[i],
"Content-Disposition": attachment_content_dispositions[i],
"FilePath": attachment_internal_path[i]
} for i in range(len(attachment_names))
],
'AttachmentNames': attachment_names if attachment_names else [],
'Format': eml.get_content_type(),
'Depth': MAX_DEPTH_CONST - max_depth
}
return email_data, attached_emails
def create_email_output(email_data, attached_emails):
# for backward compatibility if there are no attached files we return single dict
# if there are attached files then we will return array of all the emails
res = []
if email_data:
res.append(email_data)
if len(attached_emails) > 0:
res.extend(attached_emails)
if len(res) == 0:
return None
if len(res) == 1:
return res[0]
return res
def is_email_data_populated(email_data):
# checks if email data has any item populated to it
if email_data:
for key, val in email_data.iteritems():
if val:
return True
return False
def main():
file_type = ''
entry_id = demisto.args()['entryid']
max_depth = int(demisto.args().get('max_depth', '3'))
nesting_level_to_return = demisto.args().get('nesting_level_to_return', 'All files')
# we use the MAX_DEPTH_CONST to calculate the depth of the email
# each level will reduce the max_depth by 1
# not the best way to do it
global MAX_DEPTH_CONST
MAX_DEPTH_CONST = max_depth
if max_depth < 1:
return_error('Minimum max_depth is 1, the script will parse just the top email')
parse_only_headers = demisto.args().get('parse_only_headers', 'false').lower() == 'true'
try:
result = demisto.executeCommand('getFilePath', {'id': entry_id})
if is_error(result):
return_error(get_error(result))
file_path = result[0]['Contents']['path']
file_name = result[0]['Contents']['name']
result = demisto.executeCommand('getEntry', {'id': entry_id})
if is_error(result):
return_error(get_error(result))
file_metadata = result[0]['FileMetadata']
file_type = file_metadata.get('info', '') or file_metadata.get('type', '')
if 'MIME entity text, ISO-8859 text' in file_type or 'MIME entity, ISO-8859 text' in file_type:
file_type = 'application/pkcs7-mime'
except Exception as ex:
return_error(
"Failed to load file entry with entry id: {}. Error: {}".format(
entry_id, str(ex) + "\n\nTrace:\n" + traceback.format_exc()))
try:
file_type_lower = file_type.lower()
if 'composite document file v2 document' in file_type_lower \
or 'cdfv2 microsoft outlook message' in file_type_lower:
email_data, attached_emails = handle_msg(file_path, file_name, parse_only_headers, max_depth)
output = create_email_output(email_data, attached_emails)
elif any(eml_candidate in file_type_lower for eml_candidate in
['rfc 822 mail', 'smtp mail', 'multipart/signed', 'multipart/alternative', 'multipart/mixed',
'message/rfc822',
'application/pkcs7-mime', 'multipart/related']):
if 'unicode (with bom) text' in file_type_lower:
email_data, attached_emails = handle_eml(
file_path, False, file_name, parse_only_headers, max_depth, bom=True
)
else:
email_data, attached_emails = handle_eml(file_path, False, file_name, parse_only_headers, max_depth)
output = create_email_output(email_data, attached_emails)
elif ('ascii text' in file_type_lower or 'unicode text' in file_type_lower
or ('data' == file_type_lower.strip() and file_name and file_name.lower().strip().endswith('.eml'))):
try:
# Try to open the email as-is
with open(file_path, 'rb') as f:
file_contents = f.read()
if file_contents and 'Content-Type:'.lower() in file_contents.lower():
email_data, attached_emails = handle_eml(file_path, b64=False, file_name=file_name,
parse_only_headers=parse_only_headers, max_depth=max_depth)
output = create_email_output(email_data, attached_emails)
else:
# Try a base64 decode
b64decode(file_contents)
if file_contents and 'Content-Type:'.lower() in file_contents.lower():
email_data, attached_emails = handle_eml(file_path, b64=True, file_name=file_name,
parse_only_headers=parse_only_headers,
max_depth=max_depth)
output = create_email_output(email_data, attached_emails)
else:
try:
# Try to open
email_data, attached_emails = handle_eml(file_path, b64=False, file_name=file_name,
parse_only_headers=parse_only_headers,
max_depth=max_depth)
is_data_populated = is_email_data_populated(email_data)
if not is_data_populated:
raise DemistoException("No email_data found")
output = create_email_output(email_data, attached_emails)
except Exception as e:
demisto.debug("ParseEmailFiles failed with {}".format(str(e)))
return_error("Could not extract email from file. Possible reasons for this error are:\n"
"- Base64 decode did not include rfc 822 strings.\n"
"- Email contained no Content-Type and no data.")
except Exception as e:
return_error("Exception while trying to decode email from within base64: {}\n\nTrace:\n{}"
.format(str(e), traceback.format_exc()))
else:
return_error("Unknown file format: [{}] for file: [{}]".format(file_type, file_name))
output = recursive_convert_to_unicode(output)
email = output # output may be a single email
if isinstance(output, list) and len(output) > 0:
email, output = parse_nesting_level(nesting_level_to_return, output)
return_outputs(
readable_output=data_to_md(email, file_name, print_only_headers=parse_only_headers),
outputs={
'Email': output
},
raw_response=output
)
except Exception as ex:
demisto.error(str(ex) + "\n\nTrace:\n" + traceback.format_exc())
return_error(str(ex) + "\n\nTrace:\n" + traceback.format_exc())
if __name__ in ('__builtin__', '__main__'):
main()