import logging import email from flanker.mime.message.scanner import ContentType from flanker.mime.message import utils, charsets, headers from flanker.mime.message.headers import parametrized log = logging.getLogger(__name__) class FallbackMimePart(object): def __init__(self, python_message): self.m = python_message self._is_root = False def is_body(self): return (self.content_type.format_type == 'text' or self.content_type.format_type == 'message') def is_root(self): return self._is_root def set_root(self, value): self._is_root = value def append(self, message): self.m.attach( FallbackMimePart( email.message_from_string( message.to_string()))) def remove_headers(self, *headers): for header in headers: if header in self.headers: del self.headers[header] def is_bounce(self): return False @property def bounce(self): return None def to_python_message(self): return self.m def get_attached_message(self): """ Returns attached message if found, None otherwize """ try: for part in self.walk(with_self=True): if part.content_type == 'message/rfc822': for p in part.walk(): return p except Exception: log.exception("Failed to get attached message") return None def walk(self, with_self=False, skip_enclosed=False): if with_self: yield self if self.content_type.is_multipart(): for p in self.parts: yield p for x in p.walk(False, skip_enclosed=skip_enclosed): yield x elif self.content_type.is_message_container() and not skip_enclosed: yield self.enclosed for p in self.enclosed.walk(False): yield p @property def content_type(self): return ContentType( self.m.get_content_maintype(), self.m.get_content_subtype(), dict(self.m.get_params() or [])) @property def charset(self): return self.content_type.params.get('charset', 'ascii') @property def headers(self): return FallbackHeaders(self.m) @property def body(self): if not self.m.is_multipart(): return charsets.convert_to_unicode( self.charset, self.m.get_payload(decode=True)) @body.setter def body(self, value): if not self.m.is_multipart(): return self.m.set_payload( value.encode('utf-8'), 'utf-8') @property def parts(self): if self.m.is_multipart(): return [FallbackMimePart(p) for p in self.m.get_payload() if p] else: return [] @property def enclosed(self): if self.content_type == 'message/rfc822': return FallbackMimePart(self.m.get_payload()[0]) @property def size(self): if not self.m.is_multipart(): return len(self.m.get_payload(decode=False)) else: return sum(p.size for p in self.parts) @property def content_encoding(self): return self.m.get('Content-Transfer-Encoding') @content_encoding.setter def content_encoding(self, value): pass @property def content_disposition(self): try: return parametrized.decode( self.m.get('Content-Disposition', '')) except: return (None, {}) def to_string(self): return utils.python_message_to_string(self.m) def to_stream(self, out): out.write(self.to_string()) def is_attachment(self): return self.content_disposition[0] == 'attachment' def is_inline(self): return self.content_disposition[0] == 'inline' def is_delivery_notification(self): ctype = self.content_type return ctype == 'multipart/report'\ and ctype.params.get('report-type') == 'delivery-status' def __str__(self): return "FallbackMimePart" def try_decode(key, value): if isinstance(value, (tuple, list)): return value elif isinstance(value, str): try: return headers.parse_header_value(key, value) except Exception: return unicode(value, 'utf-8', 'ignore') elif isinstance(value, unicode): return value else: return "" class FallbackHeaders(object): def __init__(self, message): self.m = message def __getitem__(self, key): return try_decode(key, self.m.get(key)) def __len__(self): return len(self.m._headers) def __contains__(self, key): return key in self.m def __setitem__(self, key, value): if key in self.m: del self.m[key] self.m[key] = headers.to_mime(key, value) def __delitem__(self, key): del self.m[key] def __nonzero__(self): return len(self.m) > 0 def __iter__(self): for key, val in self.iteritems(): yield (key, val) def prepend(self, key, val): self.m._headers.insert(0, (key, val)) def add(self, key, value): self.m[key] = headers.to_mime(key, value) def keys(self): return self.m.keys() def items(self): return [(key, val) for (key, val) in self.iteritems()] def iteritems(self): for key, val in self.m.items(): yield (key, try_decode(key, val)) def get(self, key, default=None): val = try_decode(key, self.m.get(key, default)) return val if val is not None else default def getall(self, key): return [try_decode(key, v) for v in self.m.get_all(key, [])] def __str__(self): return str(self.m._headers)