Skip to content

Commit

Permalink
Merge pull request #186 from TheElementalOfDestruction/master
Browse files Browse the repository at this point in the history
v0.28.2
  • Loading branch information
TheElementalOfDestruction authored Feb 16, 2021
2 parents 60a8ad3 + 9f5ecc0 commit 0b7bb8d
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 26 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
**v0.28.2**
* Started preparing more of the code for when HTML and RTF saving are fully implemented. Please note that they do not work at all right now. Commented out the code for this because it wasn't meant to be uncommented.
* [[TeamMsgExtractor #184](https://github.com/TeamMsgExtractor/msg-extractor/issues/184)] Added code to ensure file names don't have null characters when saving an attachment.
* Minor improvement to the section of the save code that checks if you have provided incompatible options.
* [[TeamMsgExtractor #185](https://github.com/TeamMsgExtractor/msg-extractor/issues/184)] Added the `IncompatibleOptionsError`. It was supposed to be added a few updates ago, but was accidentally left out.
* Modified `Message.save` to return the current `Message` instance to allow for chained commands. This allows you to do something like `extract_msg.openMsg("path/to/message.msg").save().close()` where you could not before.

**v0.28.1**
* [[TeamMsgExtractor #181](https://github.com/TeamMsgExtractor/msg-extractor/issues/181)] Fixed issue in `Attachment` that arose when moving some of the code to a base class.
* Fixed small error in `utils.parse_type` that caused it to incorrectly compare expected and actual length. Fortunately, this had no actual effect aside from a warning.
Expand Down
4 changes: 2 additions & 2 deletions extract_msg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

__author__ = 'The Elemental of Destruction & Matthew Walker'
__date__ = '2021-01-12'
__version__ = '0.28.1'
__date__ = '2021-02-16'
__version__ = '0.28.2'

import logging

Expand Down
2 changes: 1 addition & 1 deletion extract_msg/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def main():
print(msg.body)
else:
os.chdir(out)
msg.save(toJson = args.json, useFileName = args.use_filename, ContentId = args.cid)#, html = args.html, rtf = args.html)
msg.save(toJson = args.json, useFileName = args.use_filename, ContentId = args.cid)#, html = args.html, rtf = args.html, args.allowFallback)
except Exception as e:
print("Error with file '" + x[0] + "': " +
traceback.format_exc())
Expand Down
15 changes: 9 additions & 6 deletions extract_msg/attachment.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from extract_msg.named import NamedAttachmentProperties
from extract_msg.prop import FixedLengthProp, VariableLengthProp
from extract_msg.properties import Properties
from extract_msg.utils import openMsg, verifyPropertyId, verifyType
from extract_msg.utils import openMsg, inputToString, verifyPropertyId, verifyType

logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
Expand Down Expand Up @@ -48,8 +48,8 @@ def __init__(self, msg, dir_):
else:
raise TypeError('Unknown attachment type.')

def save(self, contentId = False, json = False, useFileName = False, raw = False, customPath = None, customFilename = None,
html = False, rtf = False):
def save(self, contentId = False, json = False, useFileName = False, raw = False, customPath = None,
customFilename = None):#, html = False, rtf = False, allowFallback = False):
# Check if the user has specified a custom filename
filename = None
if customFilename is not None and customFilename != '':
Expand All @@ -76,20 +76,23 @@ def save(self, contentId = False, json = False, useFileName = False, raw = False
customPath += '/'
filename = customPath + filename

# Someone managed to have a null character here, so let's get rid of that
filename = inputToString(filename, self.msg.stringEncoding).replace('\x00', '')

if self.__type == "data":
with open(filename, 'wb') as f:
f.write(self.__data)
else:
self.saveEmbededMessage(contentId, json, useFileName, raw, customPath, customFilename, html, rtf)
self.saveEmbededMessage(contentId, json, useFileName, raw, customPath, customFilename)#, html, rtf, allowFallback)
return filename

def saveEmbededMessage(self, contentId = False, json = False, useFileName = False, raw = False, customPath = None,
customFilename = None, html = False, rtf = False):
customFilename = None):#, html = False, rtf = False, allowFallback = False):
"""
Seperate function from save to allow it to
easily be overridden by a subclass.
"""
self.data.save(json, useFileName, raw, contentId, customPath, customFilename, html, rtf)
self.data.save(json, useFileName, raw, contentId, customPath, customFilename)#, html, rtf, allowFallback)

@property
def cid(self):
Expand Down
11 changes: 11 additions & 0 deletions extract_msg/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ class ConversionError(Exception):
"""
pass

class DataNotFoundError(Exception):
"""
Requested stream type was unavailable.
"""
pass

class IncompatibleOptionsError(Exception):
"""
Provided options are incompatible with each other.
"""

class InvalidFileFormatError(OSError):
"""
An Invalid File Format Error occurred.
Expand Down
46 changes: 35 additions & 11 deletions extract_msg/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from extract_msg import constants
from extract_msg.attachment import Attachment
from extract_msg.compat import os_ as os
from extract_msg.exceptions import DataNotFoundError, IncompatibleOptionsError
from extract_msg.message_base import MessageBase
from extract_msg.recipient import Recipient
from extract_msg.utils import addNumToDir, inputToBytes, inputToString
Expand Down Expand Up @@ -37,10 +38,9 @@ def dump(self):
print('Body:')
print(self.body)

def save(self, toJson = False, useFileName = False, raw = False, ContentId = False, customPath = None, customFilename = None, html = False, rtf = False):
def save(self, toJson = False, useFileName = False, raw = False, ContentId = False, customPath = None, customFilename = None):#, html = False, rtf = False, allowFallback = False):
"""
Saves the message body and attachments found in the message. Setting toJson
to true will output the message body as JSON-formatted text. The body and
Saves the message body and attachments found in the message. The body and
attachments are stored in a folder. Setting useFileName to true will mean that
the filename is used as the name of the folder; otherwise, the message's date
and subject are used as the folder name.
Expand All @@ -49,14 +49,30 @@ def save(self, toJson = False, useFileName = False, raw = False, ContentId = Fal
2. self.filename if useFileName
3. {date} {subject}
"""
count = 0
count += 1 if toJson else 0
count += 1 if html else 0
count += 1 if rtf else 0
#There are several parameters used to determine how the message will be saved.
#By default, the message will be saved as plain text. Setting one of the
#following parameters to True will change that:
# * :param html: will try to output the message in HTML format.
# * :param json: will output the message in JSON format.
# * :param raw: will output the message in a raw format.
# * :param rtf: will output the message in RTF format.
#
#Usage of more than one formatting parameter will raise an exception.
#
#Using HTML or RTF will raise an exception if they could not be retrieved
#unless you have :param allowFallback: set to True. Fallback will go in this
#order, starting at the top most format that is set:
# * HTML
# * RTF
# * Plain text
#"""
count = 1 if toJson else 0
#count += 1 if html else 0
#count += 1 if rtf else 0
count += 1 if raw else 0

if count > 1:
raise IncompatibleOptionsException('Only one of the following options may be used at a time: toJSon, raw, html, rtf')
raise IncompatibleOptionsError('Only one of the following options may be used at a time: toJSon, raw, html, rtf')

crlf = inputToBytes(self.crlf, 'utf-8')

Expand Down Expand Up @@ -107,7 +123,7 @@ def save(self, toJson = False, useFileName = False, raw = False, ContentId = Fal
attachmentNames = []
# Save the attachments
for attachment in self.attachments:
attachmentNames.append(attachment.save(ContentId, toJson, useFileName, raw, html = html, rtf = rtf))
attachmentNames.append(attachment.save(ContentId, toJson, useFileName, raw))#, html = html, rtf = rtf, allowFallback = allowFallback))

# Save the message body
fext = 'json' if toJson else 'txt'
Expand All @@ -118,10 +134,15 @@ def save(self, toJson = False, useFileName = False, raw = False, ContentId = Fal
# if self.htmlBody is not None:
# useHtml = True
# fext = 'html'
#elif rtf:
# if self.htmlBody is not None:
#elif not allowFallback:
# raise DataNotFoundError('Could not find the htmlBody')

#if rtf or (html and not useHtml):
# if self.rtfBody is not None:
# useRtf = True
# fext = 'rtf'
#elif not allowFallback:
# raise DataNotFoundError('Could not find the rtfBody')

with open('message.' + fext, 'wb') as f:
if toJson:
Expand Down Expand Up @@ -158,6 +179,9 @@ def save(self, toJson = False, useFileName = False, raw = False, ContentId = Fal
# Return to previous directory
os.chdir(oldDir)

# Return the instance so that functions can easily be chained.
return self

def saveRaw(self):
# Create a 'raw' folder
oldDir = os.getcwdu()
Expand Down
15 changes: 9 additions & 6 deletions extract_msg/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from extract_msg import constants
from extract_msg.compat import os_ as os
from extract_msg.exceptions import ConversionError, InvaildPropertyIdError, UnknownCodepageError, UnknownTypeError, UnrecognizedMSGTypeError
from extract_msg.exceptions import ConversionError, IncompatibleOptionsError, InvaildPropertyIdError, UnknownCodepageError, UnknownTypeError, UnrecognizedMSGTypeError

logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
Expand Down Expand Up @@ -165,7 +165,7 @@ def get_command_args(args):
help='Changes to write output files as json.')
# --file-logging
parser.add_argument('--file-logging', dest='file_logging', action='store_true',
help='Enables file logging. Implies --verbose')
help='Enables file logging. Implies --verbose.')
# --verbose
parser.add_argument('--verbose', dest='verbose', action='store_true',
help='Turns on console logging.')
Expand All @@ -186,13 +186,16 @@ def get_command_args(args):
help='Tells the program to dump the message body (plain text) to stdout. Overrides saving arguments.')
# --html
#parser.add_argument('--html', dest='html', action='store_true',
# help='Sets whether the output should be html. If this is not possible, will fallback to plain text')
# help='Sets whether the output should be html. If this is not possible, will error.')
# --rtf
#parser.add_argument('--rtf', dest='rtf', action='store_true',
# help='Sets whether the output should be rtf. If this is not possible, will fallback to plain text')
# help='Sets whether the output should be rtf. If this is not possible, will error.')
# --allow-fallback
#parser.add_argument('--allow-fallback', dest='allowFallbac', action='store_true',
# help='Tells the program to fallback to a different save type if the selected one is not possible.')
# --out-name NAME
# parser.add_argument('--out-name', dest = 'out_name',
# help = 'Name to be used with saving the file output. Should come immediately after the file name')
# help = 'Name to be used with saving the file output. Should come immediately after the file name.')
# [msg files]
parser.add_argument('msgs', metavar='msg', nargs='+',
help='An msg file to be parsed')
Expand All @@ -207,7 +210,7 @@ def get_command_args(args):
#if options.json:
# valid += 1
#if valid > 1:
# raise Exception('Only one of these options may be selected at a time: --html, --rtf, --json')
# raise IncompatibleOptionsError('Only one of these options may be selected at a time: --html, --rtf, --json')

if options.dev or options.file_logging:
options.verbose = True
Expand Down

0 comments on commit 0b7bb8d

Please sign in to comment.