188 lines
5.1 KiB
Python
188 lines
5.1 KiB
Python
import logging
|
|
|
|
from extract_msg import constants
|
|
from extract_msg.utils import properHex
|
|
|
|
logger = logging.getLogger(__name__)
|
|
logger.addHandler(logging.NullHandler())
|
|
|
|
|
|
def create_prop(string):
|
|
temp = constants.ST2.unpack(string)[0]
|
|
if temp in constants.FIXED_LENGTH_PROPS:
|
|
return FixedLengthProp(string)
|
|
else:
|
|
if temp not in constants.VARIABLE_LENGTH_PROPS:
|
|
# DEBUG
|
|
logger.warning('Unknown property type: {}'.format(properHex(temp)))
|
|
return VariableLengthProp(string)
|
|
|
|
|
|
class PropBase(object):
|
|
"""
|
|
Base class for Prop instances.
|
|
"""
|
|
|
|
def __init__(self, string):
|
|
super(PropBase, self).__init__()
|
|
self.__raw = string
|
|
self.__name = properHex(string[3::-1]).upper()
|
|
self.__type, self.__flags = constants.ST2.unpack(string)
|
|
self.__fm = self.__flags & 1 == 1
|
|
self.__fr = self.__flags & 2 == 2
|
|
self.__fw = self.__flags & 4 == 4
|
|
|
|
@property
|
|
def flag_mandatory(self):
|
|
"""
|
|
Boolean, is the "mandatory" flag set?
|
|
"""
|
|
return self.__fm
|
|
|
|
@property
|
|
def flag_readable(self):
|
|
"""
|
|
Boolean, is the "readable" flag set?
|
|
"""
|
|
return self.__fr
|
|
|
|
@property
|
|
def flag_writable(self):
|
|
"""
|
|
Boolean, is the "writable" flag set?
|
|
"""
|
|
return self.__fw
|
|
|
|
@property
|
|
def flags(self):
|
|
"""
|
|
Integer that contains property flags.
|
|
"""
|
|
return self.__flags
|
|
|
|
@property
|
|
def name(self):
|
|
"""
|
|
Property "name".
|
|
"""
|
|
return self.__name
|
|
|
|
@property
|
|
def raw(self):
|
|
"""
|
|
Raw binary string that defined the property.
|
|
"""
|
|
return self.__raw
|
|
|
|
@property
|
|
def type(self):
|
|
"""
|
|
The type of property.
|
|
"""
|
|
return self.__type
|
|
|
|
|
|
class FixedLengthProp(PropBase):
|
|
"""
|
|
Class to contain the data for a single fixed length property.
|
|
|
|
Currently a work in progress.
|
|
"""
|
|
|
|
def __init__(self, string):
|
|
super(FixedLengthProp, self).__init__(string)
|
|
self.__value = self.parse_type(self.type, constants.STFIX.unpack(string)[0])
|
|
|
|
def parse_type(self, _type, stream):
|
|
"""
|
|
Converts the data in :param stream: to a
|
|
much more accurate type, specified by
|
|
:param _type:, if possible.
|
|
:param stream: #TODO what is stream for?
|
|
|
|
WARNING: Not done.
|
|
"""
|
|
# WARNING Not done.
|
|
value = stream
|
|
if _type == 0x0000: # PtypUnspecified
|
|
pass
|
|
elif _type == 0x0001: # PtypNull
|
|
if value != b'\x00\x00\x00\x00\x00\x00\x00\x00':
|
|
# DEBUG
|
|
logger.warning('Property type is PtypNull, but is not equal to 0.')
|
|
value = None
|
|
elif _type == 0x0002: # PtypInteger16
|
|
value = constants.STI16.unpack(value)[0]
|
|
elif _type == 0x0003: # PtypInteger32
|
|
value = constants.STI32.unpack(value)[0]
|
|
elif _type == 0x0004: # PtypFloating32
|
|
value = constants.STF32.unpack(value)[0]
|
|
elif _type == 0x0005: # PtypFloating64
|
|
value = constants.STF64.unpack(value)[0]
|
|
elif _type == 0x0006: # PtypCurrency
|
|
value = (constants.STI64.unpack(value))[0] / 10000.0
|
|
elif _type == 0x0007: # PtypFloatingTime
|
|
value = constants.STF64.unpack(value)[0]
|
|
# TODO parsing for this
|
|
pass
|
|
elif _type == 0x000A: # PtypErrorCode
|
|
value = constants.STI32.unpack(value)[0]
|
|
# TODO parsing for this
|
|
pass
|
|
elif _type == 0x000B: # PtypBoolean
|
|
value = bool(constants.ST3.unpack(value)[0])
|
|
elif _type == 0x0014: # PtypInteger64
|
|
value = constants.STI64.unpack(value)[0]
|
|
elif _type == 0x0040: # PtypTime
|
|
value = constants.ST3.unpack(value)[0]
|
|
elif _type == 0x0048: # PtypGuid
|
|
# TODO parsing for this
|
|
pass
|
|
return value
|
|
|
|
@property
|
|
def value(self):
|
|
"""
|
|
Property value.
|
|
"""
|
|
return self.__value
|
|
|
|
|
|
class VariableLengthProp(PropBase):
|
|
"""
|
|
Class to contain the data for a single variable length property.
|
|
"""
|
|
|
|
def __init__(self, string):
|
|
super(VariableLengthProp, self).__init__(string)
|
|
self.__length, self.__reserved = constants.STVAR.unpack(string)
|
|
if self.type == 0x001E:
|
|
self.__realLength = self.__length - 1
|
|
elif self.type == 0x001F:
|
|
self.__realLength = self.__length - 2
|
|
elif self.type == 0x000D:
|
|
self.__realLength = None
|
|
else:
|
|
self.__realLength = self.__length
|
|
|
|
@property
|
|
def length(self):
|
|
"""
|
|
The length field of the variable length property.
|
|
"""
|
|
return self.__length
|
|
|
|
@property
|
|
def reserved_flags(self):
|
|
"""
|
|
The reserved flags field of the variable length property.
|
|
"""
|
|
return self.__reserved
|
|
|
|
@property
|
|
def real_length(self):
|
|
"""
|
|
The ACTUAL length of the stream that this property corresponds to.
|
|
"""
|
|
return self.__realLength
|