|
Packit Service |
a04d08 |
# Copyright (C) 2012 Canonical Ltd.
|
|
Packit Service |
a04d08 |
# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
|
|
Packit Service |
a04d08 |
# Copyright (C) 2012 Yahoo! Inc.
|
|
Packit Service |
a04d08 |
#
|
|
Packit Service |
a04d08 |
# Author: Scott Moser <scott.moser@canonical.com>
|
|
Packit Service |
a04d08 |
# Author: Juerg Haefliger <juerg.haefliger@hp.com>
|
|
Packit Service |
a04d08 |
# Author: Joshua Harlow <harlowja@yahoo-inc.com>
|
|
Packit Service |
a04d08 |
#
|
|
Packit Service |
a04d08 |
# This file is part of cloud-init. See LICENSE file for license information.
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
import os
|
|
Packit Service |
a04d08 |
from email.mime.base import MIMEBase
|
|
Packit Service |
a04d08 |
from email.mime.multipart import MIMEMultipart
|
|
Packit Service |
a04d08 |
from email.mime.nonmultipart import MIMENonMultipart
|
|
Packit Service |
a04d08 |
from email.mime.text import MIMEText
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
from cloudinit import handlers
|
|
Packit Service |
a04d08 |
from cloudinit import log as logging
|
|
Packit Service |
751c4a |
from cloudinit import features
|
|
Packit Service |
a04d08 |
from cloudinit.url_helper import read_file_or_url, UrlError
|
|
Packit Service |
a04d08 |
from cloudinit import util
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
LOG = logging.getLogger(__name__)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Constants copied in from the handler module
|
|
Packit Service |
a04d08 |
NOT_MULTIPART_TYPE = handlers.NOT_MULTIPART_TYPE
|
|
Packit Service |
a04d08 |
PART_FN_TPL = handlers.PART_FN_TPL
|
|
Packit Service |
a04d08 |
OCTET_TYPE = handlers.OCTET_TYPE
|
|
Packit Service |
751c4a |
INCLUDE_MAP = handlers.INCLUSION_TYPES_MAP
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Saves typing errors
|
|
Packit Service |
a04d08 |
CONTENT_TYPE = 'Content-Type'
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Various special content types that cause special actions
|
|
Packit Service |
a04d08 |
TYPE_NEEDED = ["text/plain", "text/x-not-multipart"]
|
|
Packit Service |
a04d08 |
INCLUDE_TYPES = ['text/x-include-url', 'text/x-include-once-url']
|
|
Packit Service |
a04d08 |
ARCHIVE_TYPES = ["text/cloud-config-archive"]
|
|
Packit Service |
a04d08 |
UNDEF_TYPE = "text/plain"
|
|
Packit Service |
a04d08 |
ARCHIVE_UNDEF_TYPE = "text/cloud-config"
|
|
Packit Service |
a04d08 |
ARCHIVE_UNDEF_BINARY_TYPE = "application/octet-stream"
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# This seems to hit most of the gzip possible content types.
|
|
Packit Service |
a04d08 |
DECOMP_TYPES = [
|
|
Packit Service |
a04d08 |
'application/gzip',
|
|
Packit Service |
a04d08 |
'application/gzip-compressed',
|
|
Packit Service |
a04d08 |
'application/gzipped',
|
|
Packit Service |
a04d08 |
'application/x-compress',
|
|
Packit Service |
a04d08 |
'application/x-compressed',
|
|
Packit Service |
a04d08 |
'application/x-gunzip',
|
|
Packit Service |
a04d08 |
'application/x-gzip',
|
|
Packit Service |
a04d08 |
'application/x-gzip-compressed',
|
|
Packit Service |
a04d08 |
]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Msg header used to track attachments
|
|
Packit Service |
a04d08 |
ATTACHMENT_FIELD = 'Number-Attachments'
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Only the following content types can have there launch index examined
|
|
Packit Service |
a04d08 |
# in there payload, evey other content type can still provide a header
|
|
Packit Service |
a04d08 |
EXAMINE_FOR_LAUNCH_INDEX = ["text/cloud-config"]
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _replace_header(msg, key, value):
|
|
Packit Service |
a04d08 |
del msg[key]
|
|
Packit Service |
a04d08 |
msg[key] = value
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _set_filename(msg, filename):
|
|
Packit Service |
a04d08 |
del msg['Content-Disposition']
|
|
Packit Service |
a04d08 |
msg.add_header('Content-Disposition',
|
|
Packit Service |
a04d08 |
'attachment', filename=str(filename))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
def _handle_error(error_message, source_exception=None):
|
|
Packit Service |
751c4a |
if features.ERROR_ON_USER_DATA_FAILURE:
|
|
Packit Service |
751c4a |
raise Exception(error_message) from source_exception
|
|
Packit Service |
751c4a |
else:
|
|
Packit Service |
751c4a |
LOG.warning(error_message)
|
|
Packit Service |
751c4a |
|
|
Packit Service |
751c4a |
|
|
Packit Service |
a04d08 |
class UserDataProcessor(object):
|
|
Packit Service |
a04d08 |
def __init__(self, paths):
|
|
Packit Service |
a04d08 |
self.paths = paths
|
|
Packit Service |
a04d08 |
self.ssl_details = util.fetch_ssl_details(paths)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def process(self, blob):
|
|
Packit Service |
a04d08 |
accumulating_msg = MIMEMultipart()
|
|
Packit Service |
a04d08 |
if isinstance(blob, list):
|
|
Packit Service |
a04d08 |
for b in blob:
|
|
Packit Service |
a04d08 |
self._process_msg(convert_string(b), accumulating_msg)
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
self._process_msg(convert_string(blob), accumulating_msg)
|
|
Packit Service |
a04d08 |
return accumulating_msg
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _process_msg(self, base_msg, append_msg):
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def find_ctype(payload):
|
|
Packit Service |
a04d08 |
return handlers.type_from_starts_with(payload)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
for part in base_msg.walk():
|
|
Packit Service |
a04d08 |
if is_skippable(part):
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
ctype = None
|
|
Packit Service |
a04d08 |
ctype_orig = part.get_content_type()
|
|
Packit Service |
a04d08 |
payload = util.fully_decoded_payload(part)
|
|
Packit Service |
a04d08 |
was_compressed = False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# When the message states it is of a gzipped content type ensure
|
|
Packit Service |
a04d08 |
# that we attempt to decode said payload so that the decompressed
|
|
Packit Service |
a04d08 |
# data can be examined (instead of the compressed data).
|
|
Packit Service |
a04d08 |
if ctype_orig in DECOMP_TYPES:
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
payload = util.decomp_gzip(payload, quiet=False)
|
|
Packit Service |
a04d08 |
# At this point we don't know what the content-type is
|
|
Packit Service |
a04d08 |
# since we just decompressed it.
|
|
Packit Service |
a04d08 |
ctype_orig = None
|
|
Packit Service |
a04d08 |
was_compressed = True
|
|
Packit Service |
a04d08 |
except util.DecompressionError as e:
|
|
Packit Service |
751c4a |
error_message = (
|
|
Packit Service |
751c4a |
"Failed decompressing payload from {} of"
|
|
Packit Service |
751c4a |
" length {} due to: {}".format(
|
|
Packit Service |
751c4a |
ctype_orig, len(payload), e))
|
|
Packit Service |
751c4a |
_handle_error(error_message, e)
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Attempt to figure out the payloads content-type
|
|
Packit Service |
a04d08 |
if not ctype_orig:
|
|
Packit Service |
a04d08 |
ctype_orig = UNDEF_TYPE
|
|
Packit Service |
751c4a |
# There are known cases where mime-type text/x-shellscript included
|
|
Packit Service |
751c4a |
# non shell-script content that was user-data instead. It is safe
|
|
Packit Service |
751c4a |
# to check the true MIME type for x-shellscript type since all
|
|
Packit Service |
751c4a |
# shellscript payloads must have a #! header. The other MIME types
|
|
Packit Service |
751c4a |
# that cloud-init supports do not have the same guarantee.
|
|
Packit Service |
751c4a |
if ctype_orig in TYPE_NEEDED + ['text/x-shellscript']:
|
|
Packit Service |
a04d08 |
ctype = find_ctype(payload)
|
|
Packit Service |
a04d08 |
if ctype is None:
|
|
Packit Service |
a04d08 |
ctype = ctype_orig
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# In the case where the data was compressed, we want to make sure
|
|
Packit Service |
a04d08 |
# that we create a new message that contains the found content
|
|
Packit Service |
a04d08 |
# type with the uncompressed content since later traversals of the
|
|
Packit Service |
a04d08 |
# messages will expect a part not compressed.
|
|
Packit Service |
a04d08 |
if was_compressed:
|
|
Packit Service |
a04d08 |
maintype, subtype = ctype.split("/", 1)
|
|
Packit Service |
a04d08 |
n_part = MIMENonMultipart(maintype, subtype)
|
|
Packit Service |
a04d08 |
n_part.set_payload(payload)
|
|
Packit Service |
a04d08 |
# Copy various headers from the old part to the new one,
|
|
Packit Service |
a04d08 |
# but don't include all the headers since some are not useful
|
|
Packit Service |
a04d08 |
# after decoding and decompression.
|
|
Packit Service |
a04d08 |
if part.get_filename():
|
|
Packit Service |
a04d08 |
_set_filename(n_part, part.get_filename())
|
|
Packit Service |
a04d08 |
for h in ('Launch-Index',):
|
|
Packit Service |
a04d08 |
if h in part:
|
|
Packit Service |
a04d08 |
_replace_header(n_part, h, str(part[h]))
|
|
Packit Service |
a04d08 |
part = n_part
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if ctype != ctype_orig:
|
|
Packit Service |
a04d08 |
_replace_header(part, CONTENT_TYPE, ctype)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if ctype in INCLUDE_TYPES:
|
|
Packit Service |
a04d08 |
self._do_include(payload, append_msg)
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if ctype in ARCHIVE_TYPES:
|
|
Packit Service |
a04d08 |
self._explode_archive(payload, append_msg)
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# TODO(harlowja): Should this be happening, shouldn't
|
|
Packit Service |
a04d08 |
# the part header be modified and not the base?
|
|
Packit Service |
a04d08 |
_replace_header(base_msg, CONTENT_TYPE, ctype)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
self._attach_part(append_msg, part)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _attach_launch_index(self, msg):
|
|
Packit Service |
a04d08 |
header_idx = msg.get('Launch-Index', None)
|
|
Packit Service |
a04d08 |
payload_idx = None
|
|
Packit Service |
a04d08 |
if msg.get_content_type() in EXAMINE_FOR_LAUNCH_INDEX:
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
# See if it has a launch-index field
|
|
Packit Service |
a04d08 |
# that might affect the final header
|
|
Packit Service |
a04d08 |
payload = util.load_yaml(msg.get_payload(decode=True))
|
|
Packit Service |
a04d08 |
if payload:
|
|
Packit Service |
a04d08 |
payload_idx = payload.get('launch-index')
|
|
Packit Service |
a04d08 |
except Exception:
|
|
Packit Service |
a04d08 |
pass
|
|
Packit Service |
a04d08 |
# Header overrides contents, for now (?) or the other way around?
|
|
Packit Service |
a04d08 |
if header_idx is not None:
|
|
Packit Service |
a04d08 |
payload_idx = header_idx
|
|
Packit Service |
a04d08 |
# Nothing found in payload, use header (if anything there)
|
|
Packit Service |
a04d08 |
if payload_idx is None:
|
|
Packit Service |
a04d08 |
payload_idx = header_idx
|
|
Packit Service |
a04d08 |
if payload_idx is not None:
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
msg.add_header('Launch-Index', str(int(payload_idx)))
|
|
Packit Service |
a04d08 |
except (ValueError, TypeError):
|
|
Packit Service |
a04d08 |
pass
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _get_include_once_filename(self, entry):
|
|
Packit Service |
a04d08 |
entry_fn = util.hash_blob(entry, 'md5', 64)
|
|
Packit Service |
a04d08 |
return os.path.join(self.paths.get_ipath_cur('data'),
|
|
Packit Service |
a04d08 |
'urlcache', entry_fn)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _process_before_attach(self, msg, attached_id):
|
|
Packit Service |
a04d08 |
if not msg.get_filename():
|
|
Packit Service |
a04d08 |
_set_filename(msg, PART_FN_TPL % (attached_id))
|
|
Packit Service |
a04d08 |
self._attach_launch_index(msg)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _do_include(self, content, append_msg):
|
|
Packit Service |
a04d08 |
# Include a list of urls, one per line
|
|
Packit Service |
a04d08 |
# also support '#include <url here>'
|
|
Packit Service |
a04d08 |
# or #include-once '<url here>'
|
|
Packit Service |
a04d08 |
include_once_on = False
|
|
Packit Service |
a04d08 |
for line in content.splitlines():
|
|
Packit Service |
a04d08 |
lc_line = line.lower()
|
|
Packit Service |
a04d08 |
if lc_line.startswith("#include-once"):
|
|
Packit Service |
a04d08 |
line = line[len("#include-once"):].lstrip()
|
|
Packit Service |
a04d08 |
# Every following include will now
|
|
Packit Service |
a04d08 |
# not be refetched.... but will be
|
|
Packit Service |
a04d08 |
# re-read from a local urlcache (if it worked)
|
|
Packit Service |
a04d08 |
include_once_on = True
|
|
Packit Service |
a04d08 |
elif lc_line.startswith("#include"):
|
|
Packit Service |
a04d08 |
line = line[len("#include"):].lstrip()
|
|
Packit Service |
a04d08 |
# Disable the include once if it was on
|
|
Packit Service |
a04d08 |
# if it wasn't, then this has no effect.
|
|
Packit Service |
a04d08 |
include_once_on = False
|
|
Packit Service |
a04d08 |
if line.startswith("#"):
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
include_url = line.strip()
|
|
Packit Service |
a04d08 |
if not include_url:
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
include_once_fn = None
|
|
Packit Service |
a04d08 |
content = None
|
|
Packit Service |
a04d08 |
if include_once_on:
|
|
Packit Service |
a04d08 |
include_once_fn = self._get_include_once_filename(include_url)
|
|
Packit Service |
a04d08 |
if include_once_on and os.path.isfile(include_once_fn):
|
|
Packit Service |
a04d08 |
content = util.load_file(include_once_fn)
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
resp = read_file_or_url(include_url, timeout=5, retries=10,
|
|
Packit Service |
a04d08 |
ssl_details=self.ssl_details)
|
|
Packit Service |
a04d08 |
if include_once_on and resp.ok():
|
|
Packit Service |
a04d08 |
util.write_file(include_once_fn, resp.contents,
|
|
Packit Service |
a04d08 |
mode=0o600)
|
|
Packit Service |
a04d08 |
if resp.ok():
|
|
Packit Service |
a04d08 |
content = resp.contents
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
751c4a |
error_message = (
|
|
Packit Service |
751c4a |
"Fetching from {} resulted in"
|
|
Packit Service |
751c4a |
" a invalid http code of {}".format(
|
|
Packit Service |
751c4a |
include_url, resp.code))
|
|
Packit Service |
751c4a |
_handle_error(error_message)
|
|
Packit Service |
a04d08 |
except UrlError as urle:
|
|
Packit Service |
a04d08 |
message = str(urle)
|
|
Packit Service |
a04d08 |
# Older versions of requests.exceptions.HTTPError may not
|
|
Packit Service |
a04d08 |
# include the errant url. Append it for clarity in logs.
|
|
Packit Service |
a04d08 |
if include_url not in message:
|
|
Packit Service |
a04d08 |
message += ' for url: {0}'.format(include_url)
|
|
Packit Service |
751c4a |
_handle_error(message, urle)
|
|
Packit Service |
a04d08 |
except IOError as ioe:
|
|
Packit Service |
751c4a |
error_message = "Fetching from {} resulted in {}".format(
|
|
Packit Service |
751c4a |
include_url, ioe)
|
|
Packit Service |
751c4a |
_handle_error(error_message, ioe)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if content is not None:
|
|
Packit Service |
a04d08 |
new_msg = convert_string(content)
|
|
Packit Service |
a04d08 |
self._process_msg(new_msg, append_msg)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _explode_archive(self, archive, append_msg):
|
|
Packit Service |
a04d08 |
entries = util.load_yaml(archive, default=[], allowed=(list, set))
|
|
Packit Service |
a04d08 |
for ent in entries:
|
|
Packit Service |
a04d08 |
# ent can be one of:
|
|
Packit Service |
a04d08 |
# dict { 'filename' : 'value', 'content' :
|
|
Packit Service |
a04d08 |
# 'value', 'type' : 'value' }
|
|
Packit Service |
a04d08 |
# filename and type not be present
|
|
Packit Service |
a04d08 |
# or
|
|
Packit Service |
a04d08 |
# scalar(payload)
|
|
Packit Service |
751c4a |
if isinstance(ent, str):
|
|
Packit Service |
a04d08 |
ent = {'content': ent}
|
|
Packit Service |
a04d08 |
if not isinstance(ent, (dict)):
|
|
Packit Service |
a04d08 |
# TODO(harlowja) raise?
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
content = ent.get('content', '')
|
|
Packit Service |
a04d08 |
mtype = ent.get('type')
|
|
Packit Service |
a04d08 |
if not mtype:
|
|
Packit Service |
a04d08 |
default = ARCHIVE_UNDEF_TYPE
|
|
Packit Service |
751c4a |
if isinstance(content, bytes):
|
|
Packit Service |
a04d08 |
default = ARCHIVE_UNDEF_BINARY_TYPE
|
|
Packit Service |
a04d08 |
mtype = handlers.type_from_starts_with(content, default)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
maintype, subtype = mtype.split('/', 1)
|
|
Packit Service |
a04d08 |
if maintype == "text":
|
|
Packit Service |
751c4a |
if isinstance(content, bytes):
|
|
Packit Service |
a04d08 |
content = content.decode()
|
|
Packit Service |
a04d08 |
msg = MIMEText(content, _subtype=subtype)
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
msg = MIMEBase(maintype, subtype)
|
|
Packit Service |
a04d08 |
msg.set_payload(content)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if 'filename' in ent:
|
|
Packit Service |
a04d08 |
_set_filename(msg, ent['filename'])
|
|
Packit Service |
a04d08 |
if 'launch-index' in ent:
|
|
Packit Service |
a04d08 |
msg.add_header('Launch-Index', str(ent['launch-index']))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
for header in list(ent.keys()):
|
|
Packit Service |
a04d08 |
if header.lower() in ('content', 'filename', 'type',
|
|
Packit Service |
a04d08 |
'launch-index', 'content-disposition',
|
|
Packit Service |
a04d08 |
ATTACHMENT_FIELD.lower(),
|
|
Packit Service |
a04d08 |
CONTENT_TYPE.lower()):
|
|
Packit Service |
a04d08 |
continue
|
|
Packit Service |
a04d08 |
msg.add_header(header, ent[header])
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
self._attach_part(append_msg, msg)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _multi_part_count(self, outer_msg, new_count=None):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Return the number of attachments to this MIMEMultipart by looking
|
|
Packit Service |
a04d08 |
at its 'Number-Attachments' header.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
if ATTACHMENT_FIELD not in outer_msg:
|
|
Packit Service |
a04d08 |
outer_msg[ATTACHMENT_FIELD] = '0'
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
if new_count is not None:
|
|
Packit Service |
a04d08 |
_replace_header(outer_msg, ATTACHMENT_FIELD, str(new_count))
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
fetched_count = 0
|
|
Packit Service |
a04d08 |
try:
|
|
Packit Service |
a04d08 |
fetched_count = int(outer_msg.get(ATTACHMENT_FIELD))
|
|
Packit Service |
a04d08 |
except (ValueError, TypeError):
|
|
Packit Service |
a04d08 |
_replace_header(outer_msg, ATTACHMENT_FIELD, str(fetched_count))
|
|
Packit Service |
a04d08 |
return fetched_count
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def _attach_part(self, outer_msg, part):
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
Attach a message to an outer message. outermsg must be a MIMEMultipart.
|
|
Packit Service |
a04d08 |
Modifies a header in the outer message to keep track of number of
|
|
Packit Service |
a04d08 |
attachments.
|
|
Packit Service |
a04d08 |
"""
|
|
Packit Service |
a04d08 |
part_count = self._multi_part_count(outer_msg)
|
|
Packit Service |
a04d08 |
self._process_before_attach(part, part_count + 1)
|
|
Packit Service |
a04d08 |
outer_msg.attach(part)
|
|
Packit Service |
a04d08 |
self._multi_part_count(outer_msg, part_count + 1)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def is_skippable(part):
|
|
Packit Service |
a04d08 |
# multipart/* are just containers
|
|
Packit Service |
a04d08 |
part_maintype = part.get_content_maintype() or ''
|
|
Packit Service |
a04d08 |
if part_maintype.lower() == 'multipart':
|
|
Packit Service |
a04d08 |
return True
|
|
Packit Service |
a04d08 |
return False
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# Coverts a raw string into a mime message
|
|
Packit Service |
a04d08 |
def convert_string(raw_data, content_type=NOT_MULTIPART_TYPE):
|
|
Packit Service |
a04d08 |
"""convert a string (more likely bytes) or a message into
|
|
Packit Service |
a04d08 |
a mime message."""
|
|
Packit Service |
a04d08 |
if not raw_data:
|
|
Packit Service |
a04d08 |
raw_data = b''
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
def create_binmsg(data, content_type):
|
|
Packit Service |
a04d08 |
maintype, subtype = content_type.split("/", 1)
|
|
Packit Service |
a04d08 |
msg = MIMEBase(maintype, subtype)
|
|
Packit Service |
a04d08 |
msg.set_payload(data)
|
|
Packit Service |
a04d08 |
return msg
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
751c4a |
if isinstance(raw_data, str):
|
|
Packit Service |
a04d08 |
bdata = raw_data.encode('utf-8')
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
bdata = raw_data
|
|
Packit Service |
a04d08 |
bdata = util.decomp_gzip(bdata, decode=False)
|
|
Packit Service |
a04d08 |
if b"mime-version:" in bdata[0:4096].lower():
|
|
Packit Service |
a04d08 |
msg = util.message_from_string(bdata.decode('utf-8'))
|
|
Packit Service |
a04d08 |
else:
|
|
Packit Service |
a04d08 |
msg = create_binmsg(bdata, content_type)
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
return msg
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
|
|
Packit Service |
a04d08 |
# vi: ts=4 expandtab
|