PCQRSCANER/venv/Lib/site-packages/extract_msg/attachment.py
2019-12-22 21:51:47 +01:00

181 lines
5.9 KiB
Python

import logging
import random
import string
from extract_msg import constants
from extract_msg.properties import Properties
from extract_msg.utils import properHex
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
class Attachment(object):
"""
Stores the attachment data of a Message instance.
Should the attachment be an embeded message, the
class used to create it will be the same as the
Message class used to create the attachment.
"""
def __init__(self, msg, dir_):
"""
:param msg: the Message instance that the attachment belongs to.
:param dir_: the directory inside the msg file where the attachment is located.
"""
object.__init__(self)
self.__msg = msg
self.__dir = dir_
self.__props = Properties(self._getStream('__properties_version1.0'),
constants.TYPE_ATTACHMENT)
# Get long filename
self.__longFilename = self._getStringStream('__substg1.0_3707')
# Get short filename
self.__shortFilename = self._getStringStream('__substg1.0_3704')
# Get Content-ID
self.__cid = self._getStringStream('__substg1.0_3712')
# Get attachment data
if self.Exists('__substg1.0_37010102'):
self.__type = 'data'
self.__data = self._getStream('__substg1.0_37010102')
elif self.Exists('__substg1.0_3701000D'):
if (self.__props['37050003'].value & 0x7) != 0x5:
raise NotImplementedError(
'Current version of extract_msg does not support extraction of containers that are not embedded msg files.')
# TODO add implementation
else:
self.__prefix = msg.prefixList + [dir_, '__substg1.0_3701000D']
self.__type = 'msg'
self.__data = msg.__class__(self.msg.path, self.__prefix, self.__class__)
else:
# TODO Handling for special attacment types (like 0x00000007)
raise TypeError('Unknown attachment type.')
def _getStream(self, filename):
return self.__msg._getStream([self.__dir, filename])
def _getStringStream(self, filename):
"""
Gets a string representation of the requested filename.
Checks for both ASCII and Unicode representations and returns
a value if possible. If there are both ASCII and Unicode
versions, then :param prefer: specifies which will be
returned.
"""
return self.__msg._getStringStream([self.__dir, filename])
def Exists(self, filename):
"""
Checks if stream exists inside the attachment folder.
"""
return self.__msg.Exists([self.__dir, filename])
def sExists(self, filename):
"""
Checks if the string stream exists inside the attachment folder.
"""
return self.__msg.sExists([self.__dir, filename])
def save(self, contentId=False, json=False, useFileName=False, raw=False, customPath=None, customFilename=None):
# Check if the user has specified a custom filename
filename = None
if customFilename is not None and customFilename != '':
filename = customFilename
else:
# If not...
# Check if user wants to save the file under the Content-id
if contentId:
filename = self.__cid
# If filename is None at this point, use long filename as first preference
if filename is None:
filename = self.__longFilename
# Otherwise use the short filename
if filename is None:
filename = self.__shortFilename
# Otherwise just make something up!
if filename is None:
filename = 'UnknownFilename ' + \
''.join(random.choice(string.ascii_uppercase + string.digits)
for _ in range(5)) + '.bin'
if customPath is not None and customPath != '':
if customPath[-1] != '/' or customPath[-1] != '\\':
customPath += '/'
filename = customPath + filename
if self.__type == "data":
with open(filename, 'wb') as f:
f.write(self.__data)
else:
self.saveEmbededMessage(contentId, json, useFileName, raw, customPath, customFilename)
return filename
def saveEmbededMessage(self, contentId=False, json=False, useFileName=False, raw=False, customPath=None,
customFilename=None):
"""
Seperate function from save to allow it to
easily be overridden by a subclass.
"""
self.data.save(json, useFileName, raw, contentId, customPath, customFilename)
@property
def cid(self):
"""
Returns the content ID of the attachment, if it exists.
"""
return self.__cid
contend_id = cid
@property
def data(self):
"""
Returns the attachment data.
"""
return self.__data
@property
def dir(self):
"""
Returns the directory inside the msg file where the attachment is located.
"""
return self.__dir
@property
def longFilename(self):
"""
Returns the long file name of the attachment, if it exists.
"""
return self.__longFilename
@property
def msg(self):
"""
Returns the Message instance the attachment belongs to.
"""
return self.__msg
@property
def props(self):
"""
Returns the Properties instance of the attachment.
"""
return self.__props
@property
def shortFilename(self):
"""
Returns the short file name of the attachment, if it exists.
"""
return self.__shortFilename
@property
def type(self):
"""
Returns the type of the data.
"""
return self.__type