181 lines
5.7 KiB
Python
181 lines
5.7 KiB
Python
|
import copy
|
||
|
import logging
|
||
|
|
||
|
from extract_msg import constants
|
||
|
from extract_msg.prop import create_prop
|
||
|
from extract_msg.utils import divide, fromTimeStamp, msgEpoch, properHex
|
||
|
|
||
|
logger = logging.getLogger(__name__)
|
||
|
logger.addHandler(logging.NullHandler())
|
||
|
|
||
|
|
||
|
class Properties(object):
|
||
|
"""
|
||
|
Parser for msg properties files.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, stream, type=None, skip=None):
|
||
|
object.__init__(self)
|
||
|
self.__stream = stream
|
||
|
self.__pos = 0
|
||
|
self.__len = len(stream)
|
||
|
self.__props = {}
|
||
|
self.__naid = None
|
||
|
self.__nrid = None
|
||
|
self.__ac = None
|
||
|
self.__rc = None
|
||
|
if type is not None:
|
||
|
self.__intel = constants.INTELLIGENCE_SMART
|
||
|
if type == constants.TYPE_MESSAGE:
|
||
|
skip = 32
|
||
|
self.__naid, self.__nrid, self.__ac, self.__rc = constants.ST1.unpack(self.__stream[:24])
|
||
|
elif type == constants.TYPE_MESSAGE_EMBED:
|
||
|
skip = 24
|
||
|
self.__naid, self.__nrid, self.__ac, self.__rc = constants.ST1.unpack(self.__stream[:24])
|
||
|
else:
|
||
|
skip = 8
|
||
|
else:
|
||
|
self.__intel = constants.INTELLIGENCE_DUMB
|
||
|
if skip is None:
|
||
|
# This section of the skip handling is not very good.
|
||
|
# While it does work, it is likely to create extra
|
||
|
# properties that are created from the properties file's
|
||
|
# header data. While that won't actually mess anything
|
||
|
# up, it is far from ideal. Basically, this is the dumb
|
||
|
# skip length calculation. Preferably, we want the type
|
||
|
# to have been specified so all of the additional fields
|
||
|
# will have been filled out
|
||
|
skip = self.__len % 16
|
||
|
if skip == 0:
|
||
|
skip = 32
|
||
|
streams = divide(self.__stream[skip:], 16)
|
||
|
for st in streams:
|
||
|
a = create_prop(st)
|
||
|
self.__props[a.name] = a
|
||
|
self.__pl = len(self.__props)
|
||
|
|
||
|
def get(self, name):
|
||
|
"""
|
||
|
Retrieve the property of :param name:.
|
||
|
"""
|
||
|
try:
|
||
|
return self.__props[name]
|
||
|
except KeyError:
|
||
|
# DEBUG
|
||
|
logger.debug('KeyError exception.')
|
||
|
logger.debug(properHex(self.__stream))
|
||
|
logger.debug(self.__props)
|
||
|
raise
|
||
|
|
||
|
def has_key(self, key):
|
||
|
"""
|
||
|
Checks if :param key: is a key in the properties dictionary.
|
||
|
"""
|
||
|
return key in self.__props
|
||
|
|
||
|
def items(self):
|
||
|
return self.__props.items()
|
||
|
|
||
|
def keys(self):
|
||
|
return self.__props.keys()
|
||
|
|
||
|
def values(self):
|
||
|
return self.__props.values()
|
||
|
|
||
|
def __contains__(self, key):
|
||
|
self.__props.__contains__(key)
|
||
|
|
||
|
def __getitem__(self, key):
|
||
|
return self.__props.__getitem__(key)
|
||
|
|
||
|
def __iter__(self):
|
||
|
return self.__props.__iter__()
|
||
|
|
||
|
def __len__(self):
|
||
|
"""
|
||
|
Returns the number of properties.
|
||
|
"""
|
||
|
return self.__pl
|
||
|
|
||
|
@property
|
||
|
def __repr__(self):
|
||
|
return self.__props.__repr__
|
||
|
|
||
|
items.__doc__ = dict.items.__doc__
|
||
|
keys.__doc__ = dict.keys.__doc__
|
||
|
values.__doc__ = dict.values.__doc__
|
||
|
|
||
|
@property
|
||
|
def attachment_count(self):
|
||
|
if self.__ac is None:
|
||
|
raise TypeError('Properties instance must be intelligent and of type TYPE_MESSAGE to get attachment count.')
|
||
|
return self.__ac
|
||
|
|
||
|
@property
|
||
|
def date(self):
|
||
|
"""
|
||
|
Returns the send date contained in the Properties file.
|
||
|
"""
|
||
|
try:
|
||
|
return self.__date
|
||
|
except AttributeError:
|
||
|
if self.has_key('00390040'):
|
||
|
self.__date = fromTimeStamp(msgEpoch(self.get('00390040').value)).__format__(
|
||
|
'%a, %d %b %Y %H:%M:%S %z')
|
||
|
elif self.has_key('30080040'):
|
||
|
self.__date = fromTimeStamp(msgEpoch(self.get('30080040').value)).__format__(
|
||
|
'%a, %d %b %Y %H:%M:%S %z')
|
||
|
elif self.has_key('30070040'):
|
||
|
self.__date = fromTimeStamp(msgEpoch(self.get('30070040').value)).__format__(
|
||
|
'%a, %d %b %Y %H:%M:%S %z')
|
||
|
else:
|
||
|
# DEBUG
|
||
|
logger.warning(
|
||
|
'Error retrieving date. Setting as "Unknown". Please send the following data to developer:\n--------------------')
|
||
|
logger.warning(properHex(self.__stream))
|
||
|
logger.warning(self.keys())
|
||
|
logger.warning('--------------------')
|
||
|
self.__date = 'Unknown'
|
||
|
return self.__date
|
||
|
|
||
|
@property
|
||
|
def intelligence(self):
|
||
|
"""
|
||
|
Returns the inteligence level of the Properties instance.
|
||
|
"""
|
||
|
return self.__intel
|
||
|
|
||
|
@property
|
||
|
def next_attachment_id(self):
|
||
|
if self.__naid is None:
|
||
|
raise TypeError(
|
||
|
'Properties instance must be intelligent and of type TYPE_MESSAGE to get next attachment id.')
|
||
|
return self.__naid
|
||
|
|
||
|
@property
|
||
|
def next_recipient_id(self):
|
||
|
if self.__nrid is None:
|
||
|
raise TypeError(
|
||
|
'Properties instance must be intelligent and of type TYPE_MESSAGE to get next recipient id.')
|
||
|
return self.__nrid
|
||
|
|
||
|
@property
|
||
|
def props(self):
|
||
|
"""
|
||
|
Returns a copy of the internal properties dict.
|
||
|
"""
|
||
|
return copy.deepcopy(self.__props)
|
||
|
|
||
|
@property
|
||
|
def recipient_count(self):
|
||
|
if self.__rc is None:
|
||
|
raise TypeError('Properties instance must be intelligent and of type TYPE_MESSAGE to get recipient count.')
|
||
|
return self.__rc
|
||
|
|
||
|
@property
|
||
|
def stream(self):
|
||
|
"""
|
||
|
Returns the data stream used to generate this Properties instance.
|
||
|
"""
|
||
|
return self.__stream
|