Ejemplo n.º 1
0
    def _read_file(self, options):
        """ Dispatch to specific method to read file content, according to its mimetype or file type
            :param options : dict of reading options (quoting, separator, ...)
        """
        self.ensure_one()
        # guess mimetype from file content
        mimetype = guess_mimetype(self.file)
        (file_extension, handler,
         req) = FILE_TYPE_DICT.get(mimetype, (None, None, None))
        if handler:
            try:
                return getattr(self, '_read_' + file_extension)(options)
            except Exception:
                _logger.warn(
                    "Failed to read file '%s' (transient id %d) using guessed mimetype %s",
                    self.file_name or '<unknown>', self.id, mimetype)

        # try reading with user-provided mimetype
        (file_extension, handler,
         req) = FILE_TYPE_DICT.get(self.file_type, (None, None, None))
        if handler:
            try:
                return getattr(self, '_read_' + file_extension)(options)
            except Exception:
                _logger.warn(
                    "Failed to read file '%s' (transient id %d) using user-provided mimetype %s",
                    self.file_name or '<unknown>', self.id, self.file_type)

        # fallback on file extensions as mime types can be unreliable (e.g.
        # software setting incorrect mime types, or non-installed software
        # leading to browser not sending mime types)
        if self.file_name:
            p, ext = os.path.splitext(self.file_name)
            if ext in EXTENSIONS:
                try:
                    return getattr(self, '_read_' + ext[1:])(options)
                except Exception:
                    _logger.warn(
                        "Failed to read file '%s' (transient id %s) using file extension",
                        self.file_name, self.id)

        if req:
            raise ImportError(
                _("Unable to load \"{extension}\" file: requires Python module \"{modname}\""
                  ).format(extension=file_extension, modname=req))
        raise ValueError(
            _("Unsupported file format \"{}\", import only supports CSV, ODS, XLS and XLSX"
              ).format(self.file_type))
Ejemplo n.º 2
0
 def _compute_mimetype(self, values):
     """ compute the mimetype of the given values
         :param values : dict of values to create or write an ir_attachment
         :return mime : string indicating the mimetype, or application/octet-stream by default
     """
     mimetype = None
     if values.get('mimetype'):
         mimetype = values['mimetype']
     if not mimetype and values.get('datas_fname'):
         mimetype = mimetypes.guess_type(values['datas_fname'])[0]
     if not mimetype and values.get('url'):
         mimetype = mimetypes.guess_type(values['url'])[0]
     if values.get('datas') and (not mimetype
                                 or mimetype == 'application/octet-stream'):
         mimetype = guess_mimetype(base64.b64decode(values['datas']))
     return mimetype or 'application/octet-stream'
Ejemplo n.º 3
0
 def test_default_mimetype(self):
     mimetype = guess_mimetype(b'', default='test')
     # if available, python-magic returns application/x-empty
     self.assertIn(mimetype, ('test', 'application/x-empty'))
Ejemplo n.º 4
0
 def test_default_mimetype_empty(self):
     mimetype = guess_mimetype(b'')
     # gecoerp implementation returns application/octet-stream by default
     # if available, python-magic returns application/x-empty
     self.assertIn(mimetype,
                   ('application/octet-stream', 'application/x-empty'))
Ejemplo n.º 5
0
 def test_unknown(self):
     self.assertEqual(guess_mimetype(contents('csv')),
                      'application/octet-stream')
Ejemplo n.º 6
0
 def test_jpeg(self):
     self.assertEqual(guess_mimetype(contents('jpg')), 'image/jpeg')
Ejemplo n.º 7
0
 def test_ods(self):
     self.assertEqual(guess_mimetype(contents('ods')),
                      'application/vnd.oasis.opendocument.spreadsheet')
Ejemplo n.º 8
0
 def test_mimetype_jpg(self):
     content = base64.b64decode(JPG)
     mimetype = guess_mimetype(content, default='test')
     self.assertEqual(mimetype, 'image/jpeg')
Ejemplo n.º 9
0
 def test_xlsx(self):
     self.assertEqual(
         guess_mimetype(contents('xlsx')),
         'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
     )
Ejemplo n.º 10
0
 def test_odt(self):
     self.assertEqual(guess_mimetype(contents('odt')),
                      'application/vnd.oasis.opendocument.text')
Ejemplo n.º 11
0
 def test_docx(self):
     self.assertEqual(
         guess_mimetype(contents('docx')),
         'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
     )
Ejemplo n.º 12
0
 def test_xls(self):
     self.assertEqual(guess_mimetype(contents('xls')),
                      'application/vnd.ms-excel')
Ejemplo n.º 13
0
 def test_doc(self):
     self.assertEqual(guess_mimetype(contents('doc')), 'application/msword')
Ejemplo n.º 14
0
 def test_mimetype_octet_stream(self):
     mimetype = guess_mimetype(b'\0')
     self.assertEqual(mimetype, 'application/octet-stream')
Ejemplo n.º 15
0
 def test_zip(self):
     self.assertEqual(guess_mimetype(contents('zip')), 'application/zip')
Ejemplo n.º 16
0
 def test_mimetype_bmp(self):
     content = base64.b64decode(BMP)
     mimetype = guess_mimetype(content, default='test')
     # mimetype should match image/bmp, image/x-ms-bmp, ...
     self.assertRegexpMatches(mimetype, r'image/.*\bbmp')
Ejemplo n.º 17
0
 def test_gif(self):
     self.assertEqual(guess_mimetype(contents('gif')), 'image/gif')
Ejemplo n.º 18
0
 def test_mimetype_gif(self):
     content = base64.b64decode(GIF)
     mimetype = guess_mimetype(content, default='test')
     self.assertEqual(mimetype, 'image/gif')
Ejemplo n.º 19
0
    def binary_content(cls,
                       xmlid=None,
                       model='ir.attachment',
                       id=None,
                       field='datas',
                       unique=False,
                       filename=None,
                       filename_field='datas_fname',
                       download=False,
                       mimetype=None,
                       default_mimetype='application/octet-stream',
                       access_token=None,
                       env=None):
        """ Get file, attachment or downloadable content

        If the ``xmlid`` and ``id`` parameter is omitted, fetches the default value for the
        binary field (via ``default_get``), otherwise fetches the field for
        that precise record.

        :param str xmlid: xmlid of the record
        :param str model: name of the model to fetch the binary from
        :param int id: id of the record from which to fetch the binary
        :param str field: binary field
        :param bool unique: add a max-age for the cache control
        :param str filename: choose a filename
        :param str filename_field: if not create an filename with model-id-field
        :param bool download: apply headers to download the file
        :param str mimetype: mintype of the field (for headers)
        :param str default_mimetype: default mintype if no mintype found
        :param str access_token: optional token for unauthenticated access
                                 only available  for ir.attachment
        :param Environment env: by default use request.env
        :returns: (status, headers, content)
        """
        env = env or request.env
        # get object and content
        obj = None
        if xmlid:
            obj = env.ref(xmlid, False)
        elif id and model in env.registry:
            obj = env[model].browse(int(id))

        # obj exists
        if not obj or not obj.exists() or field not in obj:
            return (404, [], None)

        # access token grant access
        if model == 'ir.attachment' and access_token:
            obj = obj.sudo()
            if not consteq(obj.access_token or u'', access_token):
                return (403, [], None)

        # check read access
        try:
            last_update = obj['__last_update']
        except AccessError:
            return (403, [], None)

        status, headers, content = None, [], None

        # attachment by url check
        module_resource_path = None
        if model == 'ir.attachment' and obj.type == 'url' and obj.url:
            url_match = re.match("^/(\w+)/(.+)$", obj.url)
            if url_match:
                module = url_match.group(1)
                module_path = get_module_path(module)
                module_resource_path = get_resource_path(
                    module, url_match.group(2))
                if module_path and module_resource_path:
                    module_path = os.path.join(
                        os.path.normpath(module_path),
                        '')  # join ensures the path ends with '/'
                    module_resource_path = os.path.normpath(
                        module_resource_path)
                    if module_resource_path.startswith(module_path):
                        with open(module_resource_path, 'rb') as f:
                            content = base64.b64encode(f.read())
                        last_update = pycompat.text_type(
                            os.path.getmtime(module_resource_path))

            if not module_resource_path:
                module_resource_path = obj.url

            if not content:
                status = 301
                content = module_resource_path
        else:
            content = obj[field] or ''

        # filename
        if not filename:
            if filename_field in obj:
                filename = obj[filename_field]
            elif module_resource_path:
                filename = os.path.basename(module_resource_path)
            else:
                filename = "%s-%s-%s" % (obj._name, obj.id, field)

        # mimetype
        mimetype = 'mimetype' in obj and obj.mimetype or False
        if not mimetype:
            if filename:
                mimetype = mimetypes.guess_type(filename)[0]
            if not mimetype and getattr(env[model]._fields[field],
                                        'attachment', False):
                # for binary fields, fetch the ir_attachement for mimetype check
                attach_mimetype = env['ir.attachment'].search_read(
                    domain=[('res_model', '=', model), ('res_id', '=', id),
                            ('res_field', '=', field)],
                    fields=['mimetype'],
                    limit=1)
                mimetype = attach_mimetype and attach_mimetype[0]['mimetype']
            if not mimetype:
                mimetype = guess_mimetype(base64.b64decode(content),
                                          default=default_mimetype)

        headers += [('Content-Type', mimetype),
                    ('X-Content-Type-Options', 'nosniff')]

        # cache
        etag = bool(request) and request.httprequest.headers.get(
            'If-None-Match')
        retag = '"%s"' % hashlib.md5(
            pycompat.to_text(content).encode('utf-8')).hexdigest()
        status = status or (304 if etag == retag else 200)
        headers.append(('ETag', retag))
        headers.append(
            ('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0)))

        # content-disposition default name
        if download:
            headers.append(
                ('Content-Disposition', cls.content_disposition(filename)))
        return (status, headers, content)