Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
**v0.33.0**
* [[TeamMsgExtractor #212](https://github.com/TeamMsgExtractor/msg-extractor/issues/212)] Added support for Task objects.
* Added additional parameter `overrideClass` to all `_ensureSetX` type functions. This parameter is a class to initialize using the data, if provided. The data will be provided as the first argument to the `__init__` function of the class *if* the data is not `None`. If the data is done, the value set and returned from `_ensureSetX` will be `None`. This can be overriden using the second added parameter, which defaults to this behavior. Simply set `preserveNone` to `False` to force the data to be passed to the class. Keep in mind that this means the class will receive `None` as it's first parameter.
* Updated `Named` class to make it more like the dictionary it is build on top of. Specifically, added `keys`, `values`, and `items` as functions.
* Fixed issue in `Named` where old code wasn't deleted, causing some named properties to be completely omitted.
* Improved efficiency of `Named` by making it so `get` no longer copied the entire dictionary repeatedly while looking for the property.
* Fixed typo in `Attachment` that caused embedded msg files to still regenerate the `Named` instance even though they should have been using the parent's.
* Updated fields in `MSGFile` to use enums.
* Added additional properties to `MSGFile`.
* Significant cleanup.

**v0.32.0**
* [[TeamMsgExtractor #217](https://github.com/TeamMsgExtractor/msg-extractor/issues/217)] Redesigned named properties to be much more efficient. All in all, load times for MSG files are significantly improved all around.
* Named properties load dynamically.
Expand Down
14 changes: 12 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,20 @@ Credits

And thank you to everyone who has opened an issue and helped us track down those pesky bugs.

Extra
-----

Check out the new project `msg-explorer`_ that allows you to open MSG files and
explore their contents in a GUI. It is usually updated within a few days of a
major release to ensure continued support. Because of this, it is recommended to
install it to a separate environment (like a vitural env) to not interfere with
your access to the newest major version of extract-msg.

.. |License: GPL v3| image:: https://img.shields.io/badge/License-GPLv3-blue.svg
:target: LICENSE.txt

.. |PyPI3| image:: https://img.shields.io/badge/pypi-0.32.0-blue.svg
:target: https://pypi.org/project/extract-msg/0.32.0/
.. |PyPI3| image:: https://img.shields.io/badge/pypi-0.33.0-blue.svg
:target: https://pypi.org/project/extract-msg/0.33.0/

.. |PyPI2| image:: https://img.shields.io/badge/python-3.6+-brightgreen.svg
:target: https://www.python.org/downloads/release/python-367/
Expand All @@ -227,3 +236,4 @@ And thank you to everyone who has opened an issue and helped us track down those
.. _Seamus Tuohy: https://github.com/seamustuohy
.. _Discord: https://discord.com/invite/B77McRmzdc
.. _Buy Me a Coffee: https://www.buymeacoffee.com/DestructionE
.. _msg-explorer: https://pypi.org/project/msg-explorer/
7 changes: 4 additions & 3 deletions extract_msg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
https://github.com/TeamMsgExtractor/msg-extractor
"""

# --- LICENSE.txt -----------------------------------------------------------------
# --- LICENSE.txt --------------------------------------------------------------
#
# Copyright 2013-2022 Matthew Walker and Destiny Peterson
#
Expand All @@ -27,8 +27,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

__author__ = 'Destiny Peterson & Matthew Walker'
__date__ = '2022-06-07'
__version__ = '0.32.0'
__date__ = '2022-06-08'
__version__ = '0.33.0'

import logging

Expand All @@ -45,4 +45,5 @@
from .prop import createProp
from .properties import Properties
from .recipient import Recipient
from .task import Task
from .utils import openMsg, properHex
3 changes: 1 addition & 2 deletions extract_msg/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import traceback

from extract_msg import __doc__, utils
from extract_msg.message import Message


def main() -> None:
Expand All @@ -14,7 +13,7 @@ def main() -> None:
level = logging.INFO if args.verbose else logging.WARNING

# Determine where to save the files to.
currentDir = os.getcwd() # Store this incase the path changes.
currentDir = os.getcwd() # Store this in case the path changes.
if not args.zip:
if args.out_path:
if not os.path.exists(args.out_path):
Expand Down
5 changes: 1 addition & 4 deletions extract_msg/appointment.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from . import constants
from .attachment import Attachment
from .message_base import MessageBase


Expand Down Expand Up @@ -33,8 +31,7 @@ def location(self):
try:
return self.__location
except AttributeError:
self.__location = self.named.getNamedValue('8208')
self.__location = self.named.getNamedValue('0002') if self.__location is None else self.__location
self.__location = self.named.getNamedValue('8208') or self.named.getNamedValue('0002')
return self.__location

@property
Expand Down
7 changes: 3 additions & 4 deletions extract_msg/attachment.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
from . import constants
from .attachment_base import AttachmentBase
from .enums import AttachmentType
from .prop import FixedLengthProp, VariableLengthProp
from .properties import Properties
from .utils import createZipOpen, inputToString, openMsg, prepareFilename, verifyPropertyId, verifyType
from .utils import createZipOpen, inputToString, openMsg, prepareFilename


logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
Expand Down Expand Up @@ -44,7 +43,7 @@ def __init__(self, msg, dir_):
else:
self.__prefix = msg.prefixList + [dir_, '__substg1.0_3701000D']
self.__type = AttachmentType.MSG
self.__data = openMsg(self.msg.path, prefix = self.__prefix, parent = self.msg, **self.msg.kwargs)
self.__data = openMsg(self.msg.path, prefix = self.__prefix, parentMsg = self.msg, **self.msg.kwargs)
elif (self.props['37050003'].value & 0x7) == 0x7:
# TODO Handling for special attacment type 0x7
self.__type = AttachmentType.WEB
Expand Down
62 changes: 57 additions & 5 deletions extract_msg/attachment_base.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import logging

from . import constants
from .enums import PropertiesType
from .named import NamedProperties
from .prop import FixedLengthProp
from .properties import Properties
from .utils import verifyPropertyId, verifyType


logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())

Expand All @@ -30,13 +30,21 @@ def __init__(self, msg, dir_):
self.__namedProperties = NamedProperties(msg.named, self)


def _ensureSet(self, variable, streamID, stringStream = True):
def _ensureSet(self, variable, streamID, stringStream = True, **kwargs):
"""
Ensures that the variable exists, otherwise will set it using the
specified stream. After that, return said variable.

If the specified stream is not a string stream, make sure to set
:param stringStream: to False.

:param overrideClass: Class/function to use to morph the data that was
read. The data will be the first argument to the class's __init__
function or the function itself, if that is what is provided. By
default, this will be completely ignored if the value was not found.
:param preserveNone: If true (default), causes the function to ignore
:param overrideClass: when the value could not be found (is None).
If this is changed to False, then the value will be used regardless.
"""
try:
return getattr(self, variable)
Expand All @@ -45,25 +53,51 @@ def _ensureSet(self, variable, streamID, stringStream = True):
value = self._getStringStream(streamID)
else:
value = self._getStream(streamID)
# Check if we should be overriding the data type for this instance.
if kwargs:
overrideClass = kwargs.get('overrideClass')
if overrideClass is not None and (value is not None or not kwargs.get('preserveNone', True)):
value = overrideClass(value)
setattr(self, variable, value)
return value

def _ensureSetNamed(self, variable, propertyName):
def _ensureSetNamed(self, variable, propertyName, **kwargs):
"""
Ensures that the variable exists, otherwise will set it using the named
property. After that, return said variable.

:param overrideClass: Class/function to use to morph the data that was
read. The data will be the first argument to the class's __init__
function or the function itself, if that is what is provided. By
default, this will be completely ignored if the value was not found.
:param preserveNone: If true (default), causes the function to ignore
:param overrideClass: when the value could not be found (is None).
If this is changed to False, then the value will be used regardless.
"""
try:
return getattr(self, variable)
except AttributeError:
value = self.namedProperties.get(propertyName)
# Check if we should be overriding the data type for this instance.
if kwargs:
overrideClass = kwargs.get('overrideClass')
if overrideClass is not None and (value is not None or not kwargs.get('preserveNone', True)):
value = overrideClass(value)
setattr(self, variable, value)
return value

def _ensureSetProperty(self, variable, propertyName):
def _ensureSetProperty(self, variable, propertyName, **kwargs):
"""
Ensures that the variable exists, otherwise will set it using the
property. After that, return said variable.

:param overrideClass: Class/function to use to morph the data that was
read. The data will be the first argument to the class's __init__
function or the function itself, if that is what is provided. By
default, this will be completely ignored if the value was not found.
:param preserveNone: If true (default), causes the function to ignore
:param overrideClass: when the value could not be found (is None).
If this is changed to False, then the value will be used regardless.
"""
try:
return getattr(self, variable)
Expand All @@ -72,19 +106,37 @@ def _ensureSetProperty(self, variable, propertyName):
value = self.props[propertyName].value
except (KeyError, AttributeError):
value = None
# Check if we should be overriding the data type for this instance.
if kwargs:
overrideClass = kwargs.get('overrideClass')
if overrideClass is not None and (value is not None or not kwargs.get('preserveNone', True)):
value = overrideClass(value)
setattr(self, variable, value)
return value

def _ensureSetTyped(self, variable, _id):
def _ensureSetTyped(self, variable, _id, **kwargs):
"""
Like the other ensure set functions, but designed for when something
could be multiple types (where only one will be present). This way you
have no need to set the type, it will be handled for you.

:param overrideClass: Class/function to use to morph the data that was
read. The data will be the first argument to the class's __init__
function or the function itself, if that is what is provided. By
default, this will be completely ignored if the value was not found.
:param preserveNone: If true (default), causes the function to ignore
:param overrideClass: when the value could not be found (is None).
If this is changed to False, then the value will be used regardless.
"""
try:
return getattr(self, variable)
except AttributeError:
value = self._getTypedData(_id)
# Check if we should be overriding the data type for this instance.
if kwargs:
overrideClass = kwargs.get('overrideClass')
if overrideClass is not None and (value is not None or not kwargs.get('preserveNone', True)):
value = overrideClass(value)
setattr(self, variable, value)
return value

Expand Down
4 changes: 1 addition & 3 deletions extract_msg/contact.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from . import constants
from .attachment import Attachment
from .msg import MSGFile


Expand Down Expand Up @@ -113,7 +111,7 @@ def country(self):
@property
def departmentName(self):
"""
The name of the dapartment the contact works in.
The name of the department the contact works in.
"""
return self._ensureSet('_departmentName', '__substg1.0_3A18')

Expand Down
4 changes: 2 additions & 2 deletions extract_msg/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(self, data : bytes):
@property
def globalCounter(self) -> int:
"""
An unsigned ineger identifying the folder within its Store object.
An unsigned integer identifying the folder within its Store object.
"""
return self.__globalCounter

Expand All @@ -42,7 +42,7 @@ def __init__(self, data):
@property
def globalCounter(self) -> int:
"""
An unsigned ineger identifying the folder within its Store object.
An unsigned integer identifying the folder within its Store object.
"""
return self.__globalCounter

Expand Down
4 changes: 2 additions & 2 deletions extract_msg/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
logger.addHandler(logging.NullHandler())


def setupDevLogger(defaultPath=None, logfile = None, envKey='EXTRACT_MSG_LOG_CFG'):
def setupDevLogger(defaultPath = None, logfile = None, envKey = 'EXTRACT_MSG_LOG_CFG'):
utils.setupLogging(defaultPath, 5, logfile, True, envKey)


Expand Down Expand Up @@ -64,4 +64,4 @@ def main(args, argv):
logpath = x.baseFilename
except AttributeError:
pass;
print(g'Logging complete. Log has been saved to {logpath}')
print(f'Logging complete. Log has been saved to {logpath}')
1 change: 0 additions & 1 deletion extract_msg/dev_classes/attachment.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import logging

from .. import constants
from ..enums import PropertiesType
from ..properties import Properties
from ..utils import properHex
Expand Down
Loading