Source code for imap_storage.storage.email.file

"""File class"""
from datetime import datetime
from mimetypes import MimeTypes
from email import encoders
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from base64 import decodebytes
# :TODO: constructor functions are in wrong place


__all__ = [
    'file_from_local',
    'file_from_upload',
    'file_from_payload',
    'file_from_xml',
    ]


[docs]def file_from_local(path): """create File object from local path :param path: Of the local file """ from os import sep with open(path, 'rb') as file: data = file.read() file = File() file.name = path.split(sep)[-1] file.data = data file.mime = MimeTypes().guess_type(path)[0] # from os.path import getmtime # file.time = getmtime(path) return file
[docs]def file_from_upload(uploaded_file): """create File object from Django uploaded file""" file = File() # file.email = email_obj file.name = uploaded_file.name file.data = uploaded_file.read() file.mime = uploaded_file.content_type.split(';')[0] file.size = uploaded_file.size return file
[docs]def file_from_payload(email_obj, payload): """create File object from message payload""" file = File() file.email = email_obj file.name = payload['Content-Disposition'].split( 'filename=')[-1].strip('"') file.data = payload.get_payload() file.mime = payload['Content-Type'] return file
[docs]def file_from_xml(email_obj, xml): """create File object from xml structure""" file = File() file.email = email_obj file.name = xml.attrib['name'] file.mime = xml.attrib['mime'] if 'mime' in xml.attrib else '' file.size = xml.attrib['size'] file.time = float(xml.attrib['time']) file.id_ = xml.attrib['id'] return file
class File(): # :TODO: # pylint: disable=too-many-instance-attributes """Attachment class Maybe the following type later: from django.core.files import File :param filename: Filename as string :param data: Data as string """ def __init__(self): self.email = None self.name = None self.mime = None self.time = datetime.now().timestamp() self.id_ = None self._data = None self._size = None @property def data(self): """ :TODO: Get automatically """ return self._data or self.email.file_by_name(self.name) @data.setter def data(self, data): self._data = data @property def size(self): """Size of the file as timestamp""" return self._size or len(self.data) @size.setter def size(self, size): self._size = size @property def htime(self): """Size as human readable""" return datetime.fromtimestamp(self.time) @property def hsize(self): '''self.size as human readable string''' return self.human_readable_size(self.size) @property def is_binary(self): """Make sure that its really a binary string (not 100%)""" return self._binary_check(self.data) @property def mime_obj(self): """This is for adding the File to a Multipart Message :returns: self as (hopefully) correct Mime Object """ ctype = self.mime if ctype is None: # No guess could be made if self.is_binary: ctype = 'application/octet-stream' else: ctype = 'text/plain' maintype, subtype = ctype.split('/', 1) if maintype == 'text': try: msg = MIMEText(str(self.read(), 'utf-8'), _subtype=subtype) except (TypeError, UnicodeDecodeError): msg = MIMEText(str(self.read()), _subtype=subtype) elif maintype == 'image': msg = MIMEImage(self.read(), _subtype=subtype) elif maintype == 'audio': msg = MIMEAudio(self.read(), _subtype=subtype) else: msg = MIMEBase(maintype, subtype) msg.set_payload(self.read()) encoders.encode_base64(msg) msg.add_header( 'Content-Disposition', 'attachment', filename=self.name ) return msg def read(self): """Read the data of the object""" maintype, _ = self.mime.split('/') if self.mime else '', '' # subtype if maintype != 'text' and isinstance(self.data, str): return decodebytes(self.data.encode()) return self.data def as_response(self): """ TODO: if is base64...decode """ from django.http.response import HttpResponse response = HttpResponse(self.read()) response['Content-Type'] = self.mime response['Content-Disposition'] = 'attachment;filename="{}"'.format( self.name ) return response @staticmethod def human_readable_size(num, suffix='B'): '''changes the format of num into a human readable format''' num = int(num) for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: if abs(num) < 1024.0: return "%3.1f%s%s" % (num, unit, suffix) num /= 1024.0 return "%.1f%s%s" % (num, 'Yi', suffix) @staticmethod def _binary_check(binary): if isinstance(binary, str): binary = binary.encode('utf-8') textchars = bytearray({7, 8, 9, 10, 12, 13, 27} | set( range(0x20, 0x100)) - {0x7f}) return bool(binary.translate(None, textchars)) def __lt__(self, other): return self.name < other.name def __hash__(self): return hash((self.name)) def __eq__(self, other): return self.__hash__() == other.__hash__() def __ne__(self, other): return not self == other def __str__(self): return '{} ({})'.format(self.name, self.hsize)