Пример #1
0
def test_etag():
    rv = send_file(txt_path, environ)
    rv.close()
    assert rv.headers["ETag"].count("-") == 2
    rv = send_file(txt_path, environ, etag=False)
    rv.close()
    assert "ETag" not in rv.headers
    rv = send_file(txt_path, environ, etag="unique")
    rv.close()
    assert rv.headers["ETag"] == '"unique"'
Пример #2
0
def test_object_attachment_requires_name():
    with pytest.raises(TypeError, match="attachment"):
        send_file(
            io.BytesIO(b"test"), environ, mimetype="text/plain", as_attachment=True,
        )

    rv = send_file(
        io.BytesIO(b"test"), environ, as_attachment=True, download_name="test.txt",
    )
    assert rv.headers["Content-Disposition"] == f"attachment; filename=test.txt"
    rv.close()
Пример #3
0
def test_object(file_factory):
    rv = send_file(file_factory(), environ, mimetype="text/plain", use_x_sendfile=True)
    rv.direct_passthrough = False
    assert rv.data
    assert rv.mimetype == "text/plain"
    assert "x-sendfile" not in rv.headers
    rv.close()
Пример #4
0
def test_content_encoding(as_attachment):
    rv = send_file(
        txt_path, environ, download_name="logo.svgz", as_attachment=as_attachment
    )
    rv.close()
    assert rv.mimetype == "image/svg+xml"
    assert rv.content_encoding == ("gzip" if not as_attachment else None)
Пример #5
0
def test_path(path):
    rv = send_file(path, environ)
    assert rv.mimetype == "text/html"
    assert rv.direct_passthrough
    rv.direct_passthrough = False
    assert rv.data == html_path.read_bytes()
    rv.close()
Пример #6
0
def test_max_age(value, public):
    rv = send_file(txt_path, environ, max_age=value)
    rv.close()
    assert ("no-cache" in rv.headers["Cache-Control"]) != public
    assert rv.cache_control.public == public
    assert rv.cache_control.max_age == value
    assert rv.expires
    assert rv.status_code == 200
Пример #7
0
def test_non_ascii_name(name, ascii, utf8):
    rv = send_file(html_path, environ, as_attachment=True, download_name=name)
    rv.close()
    content_disposition = rv.headers["Content-Disposition"]
    assert f"filename={ascii}" in content_disposition

    if utf8:
        assert f"filename*=UTF-8''{utf8}" in content_disposition
    else:
        assert "filename*=UTF-8''" not in content_disposition
Пример #8
0
    def company_logo(self, dbname=None, **kw):
        imgname = 'logo'
        imgext = '.png'
        placeholder = functools.partial(get_resource_path, 'web', 'static',
                                        'img')
        dbname = request.db
        uid = (request.session.uid if dbname else None) or odoo.SUPERUSER_ID

        if not dbname:
            response = http.Stream.from_path(
                placeholder(imgname + imgext)).get_response()
        else:
            try:
                # create an empty registry
                registry = odoo.modules.registry.Registry(dbname)
                with registry.cursor() as cr:
                    company = int(
                        kw['company']) if kw and kw.get('company') else False
                    if company:
                        cr.execute(
                            """SELECT logo_web, write_date
                                        FROM res_company
                                       WHERE id = %s
                                   """, (company, ))
                    else:
                        cr.execute(
                            """SELECT c.logo_web, c.write_date
                                        FROM res_users u
                                   LEFT JOIN res_company c
                                          ON c.id = u.company_id
                                       WHERE u.id = %s
                                   """, (uid, ))
                    row = cr.fetchone()
                    if row and row[0]:
                        image_base64 = base64.b64decode(row[0])
                        image_data = io.BytesIO(image_base64)
                        mimetype = guess_mimetype(image_base64,
                                                  default='image/png')
                        imgext = '.' + mimetype.split('/')[1]
                        if imgext == '.svg+xml':
                            imgext = '.svg'
                        response = send_file(image_data,
                                             filename=imgname + imgext,
                                             mimetype=mimetype,
                                             mtime=row[1])
                    else:
                        response = http.Stream.from_path(
                            placeholder('nologo.png')).get_response()
            except Exception:
                response = http.Stream.from_path(
                    placeholder(imgname + imgext)).get_response()

        return response
Пример #9
0
def test_root_path(tmp_path):
    # This is a private API, it should only be used by Flask.
    d = tmp_path / "d"
    d.mkdir()
    (d / "test.txt").write_bytes(b"test")
    rv = send_file("d/test.txt", environ, _root_path=tmp_path)
    rv.direct_passthrough = False
    assert rv.data == b"test"
    rv.close()
    rv = send_from_directory("d", "test.txt", environ, _root_path=tmp_path)
    rv.direct_passthrough = False
    assert rv.data == b"test"
    rv.close()
Пример #10
0
def test_no_cache_conditional_default():
    rv = send_file(
        txt_path,
        EnvironBuilder(
            headers={"If-Modified-Since": http_date(datetime.datetime(2020, 7, 12))}
        ).get_environ(),
        last_modified=datetime.datetime(2020, 7, 11),
    )
    rv.close()
    assert "no-cache" in rv.headers["Cache-Control"]
    assert not rv.cache_control.public
    assert not rv.cache_control.max_age
    assert not rv.expires
    assert rv.status_code == 304
Пример #11
0
 def wsgi(self, environ, start_response):
     request_initial = Request(environ)
     request_initial.path = request_initial.path.replace('/..', '')
     response = Response('bad request', 400)
     page = Page('', 400)
     path = request_initial.path
     resource = join_path(Config.base_path, path)
     request = PHFSRequest(environ, path, resource)
     self.request = request
     if os.path.isdir(resource):
         # If there's a index.html inside folder, show that
         for i in self.indexes:
             if os.path.isfile(join_path(resource, i)):
                 path = join_path(path, i)
                 resource = join_path(resource, i)
     uni_param = UniParam([],
                          interpreter=self.interpreter,
                          request=request,
                          filelist=FileList([]),
                          statistics=self.statistics)
     levels_virtual = path.split('/')
     levels_real = resource.split('/')
     if resource[-1:] != '/' and os.path.isdir(resource):
         response = Response('', 302, {'Location': path + '/'})
         return self.return_response(request, response, environ,
                                     start_response)
     if not Account.can_access(
             self.get_current_account(request)[0], resource, True):
         response = self.unauth_response(request)
         return self.return_response(request, response, environ,
                                     start_response)
     if request.args.get('tpl', '') == 'list':
         uni_param.interpreter = self.itp_filelist
     if request.method == 'POST':
         if len(request.files) > 0:
             # File upload
             if not if_upload_allowed_in(request.path_real, Config):
                 response = Response('forbidden', 403)
             else:
                 upload_result = {}
                 for i in request.files:
                     single_file = request.files[i]
                     filename = purify_filename(single_file.filename)
                     if filename == '':
                         continue
                     elif filename in self.indexes:
                         upload_result[filename] = (
                             False,
                             I18n.get_string(
                                 'file_name_or_extension_forbidden'))
                         continue
                     try:
                         single_file.save(join_path(resource, filename))
                         upload_result[filename] = (True, '')
                     except Exception as e:
                         upload_result[filename] = (False, str(e))
                 page = self.interpreter.get_page(
                     'upload-results',
                     UniParam([upload_result],
                              interpreter=self.interpreter,
                              request=request,
                              statistics=self.statistics))
                 response = Response(page.content,
                                     page.status,
                                     page.headers,
                                     mimetype=mimeLib.getmime('*.html'))
             return self.return_response(request, response, environ,
                                         start_response)
         if request.form.get('action', '') == 'delete':
             if not Account.can_access(
                     self.get_current_account(request)[0], resource, False):
                 response = Response('forbidden', 403)
             else:
                 filelist = request.form.getlist('selection')
                 try:
                     for i in filelist:
                         smartremove(Config.base_path + i)
                     response = Response('ok', 200)
                 except Exception as err:
                     response = Response(str(err), 500)
             return self.return_response(request, response, environ,
                                         start_response)
     if 'mode' in request.args:
         # urlvar mode
         mode = request.args['mode']
         if mode == 'section':
             section_name = request.args.get('id', '')
             page = uni_param.interpreter.section_to_page(
                 section_name, uni_param)
             response = Response(page.content,
                                 page.status,
                                 page.headers,
                                 mimetype=mimeLib.getmime(section_name))
         elif mode == 'login':
             account_name = request.form.get('user')
             token_hash = request.form.get('passwordSHA256')
             expected_hash = hashLib.BaseHashToTokenHash(
                 Account.get_account_detail(account_name)[0],
                 request.cookies.get('HFS_SID_', '')).get()
             if account_name not in Account.accounts:
                 response = Response('username not found', 200)
             elif token_hash != expected_hash:
                 response = Response('bad password', 200)
             else:
                 sid = hashlib.sha256(
                     bytes([random.randint(0, 255)
                            for _ in range(32)])).hexdigest()
                 self.statistics.accounts[sid] = (account_name,
                                                  request.host)
                 response = Response('ok', 200)
                 response.headers.add_header(
                     'Set-Cookie', 'HFS_SID_=%s; HttpOnly; Max-Age=0' %
                     request.cookies.get('HFS_SID_', ''))
                 response.headers.add_header('Set-Cookie',
                                             'HFS_SID_=%s; HttpOnly' % sid)
         elif mode == 'logout':
             sid = request.cookies.get('HFS_SID_', '')
             if sid in self.statistics.accounts:
                 del self.statistics.accounts[sid]
             response = Response(
                 'ok', 200,
                 {'Set-Cookie': 'HFS_SID_=%s; HttpOnly; Max-Age=0' % sid})
         elif mode == 'archive':
             tmp = tempfile.TemporaryFile(mode='w+b')
             tar = tarfile.open(mode='w', fileobj=tmp)
             path_real = request.path_real_dir + '/'
             filelist = request.form.getlist('selection')
             final_list_without_dots = [
                 ((Config.base_path if x[0:1] == '/' else path_real) + x)
                 for x in filelist if x[0:1] != '.'
             ]
             final_list_with_dots = [
                 ((Config.base_path if x[0:1] == '/' else path_real) + x)
                 for x in filelist
             ]
             shown_files = final_list_without_dots if Config.hide_dots else final_list_with_dots
             for i in shown_files:
                 is_recursive = 'recursive' in request.args or bool(
                     Config.recur_archive)
                 tar.add(i, i, recursive=is_recursive)
             tar.close()  # Pointer is at the end of file
             tmp.seek(0)  # Read at start
             response = send_file(
                 tmp,
                 environ,
                 mimetype=mimeLib.getmime('*.tar'),
                 as_attachment=True,
                 download_name=os.path.basename(request.path_virtual_dir) +
                 '.selection.tar')
         return self.return_response(request, response, environ,
                                     start_response)
     elif 'search' in request.args:
         # Search, with re.findall
         directory = request.path_real_dir
         if not os.path.isdir(directory):
             response = self.not_found_response(request)
             return self.return_response(request, response, environ,
                                         start_response)
         pattern = re.compile(wildcard2re(request.args['search']), re.I)
         recursive = 'recursive' in request.args or bool(
             Config.recur_search)
         items_folder = []
         items_file = []
         if recursive:
             for dirpath, dirnames, filenames in os.walk(directory):
                 for i in dirnames:
                     if re.findall(pattern, i):
                         items_folder.append(os.path.join(dirpath, i))
                 for i in filenames:
                     if re.findall(pattern, i):
                         items_file.append(os.path.join(dirpath, i))
         else:
             for i in os.scandir(directory):
                 if re.findall(pattern, i.name):
                     if i.is_dir:
                         items_folder.append(i.path)
                     else:
                         items_file.append(i.path)
         path_real_dir = request.path_real_dir + '/'
         shown_files = [
             x for x in (items_folder + items_file) if x[0:1] != '.'
         ] if Config.hide_dots else (items_folder + items_file)
         paths = [x.replace('\\', '/') for x in shown_files]
         items = [ItemEntry(x, x, path_real_dir) for x in paths]
         filelist = FileList(items)
         uni_param.filelist = filelist
         page = uni_param.interpreter.get_page('', uni_param)
         response = Response(page.content,
                             page.status,
                             page.headers,
                             mimetype=mimeLib.getmime('*.html'))
         return self.return_response(request, response, environ,
                                     start_response)
     elif 'filter' in request.args:
         # Filter, with re.fullmatch
         directory = request.path_real_dir
         if not os.path.isdir(directory):
             response = self.not_found_response(request)
             return self.return_response(request, response, environ,
                                         start_response)
         pattern = re.compile(wildcard2re(request.args['filter']), re.I)
         recursive = 'recursive' in request.args or bool(
             Config.recur_search)
         items_folder = []
         items_file = []
         if recursive:
             for dirpath, dirnames, filenames in os.walk(directory):
                 for i in dirnames:
                     if re.fullmatch(pattern, i):
                         items_folder.append(os.path.join(dirpath, i))
                 for i in filenames:
                     if re.fullmatch(pattern, i):
                         items_file.append(os.path.join(dirpath, i))
         else:
             for i in os.scandir(directory):
                 if re.fullmatch(pattern, i.name):
                     if i.is_dir:
                         items_folder.append(i.path)
                     else:
                         items_file.append(i.path)
         path_real_dir = request.path_real_dir + '/'
         shown_files = [
             x for x in (items_folder + items_file) if x[0:1] != '.'
         ] if Config.hide_dots else (items_folder + items_file)
         paths = [x.replace('\\', '/') for x in shown_files]
         items = [ItemEntry(x, x, path_real_dir) for x in paths]
         filelist = FileList(items)
         uni_param.filelist = filelist
         page = uni_param.interpreter.get_page('', uni_param)
         response = Response(page.content,
                             page.status,
                             page.headers,
                             mimetype=mimeLib.getmime('*.html'))
         return self.return_response(request, response, environ,
                                     start_response)
     elif levels_virtual[-1][0:1] == '~':
         # Command
         command = levels_virtual[-1][1:]
         if len(levels_virtual) == 2:
             # Section call, only at root
             global builtin_sections
             section = uni_param.interpreter.get_section(
                 command, uni_param, True, False)
             if section != None:
                 page = Page(section.content, 200)
                 response = Response(page.content,
                                     page.status,
                                     page.headers,
                                     mimetype=mimeLib.getmime(path))
             elif command in builtin_sections:
                 response = send_file(command, environ,
                                      mimeLib.getmime(command))
             else:
                 response = self.not_found_response(request)
         if command == 'upload' and if_upload_allowed_in(
                 request.path_real, Config):
             page = uni_param.interpreter.get_page('upload', uni_param)
             response = Response(page.content,
                                 page.status,
                                 page.headers,
                                 mimetype=mimeLib.getmime('*.html'))
         elif command == 'folder.tar':
             tmp = tempfile.TemporaryFile(mode='w+b')
             tar = tarfile.open(mode='w', fileobj=tmp)
             path_real = request.path_real_dir + '/'
             shown_files = [
                 x for x in os.listdir(request.path_real_dir)
                 if x[0:1] != '.'
             ] if Config.hide_dots else os.listdir(request.path_real_dir)
             for i in shown_files:
                 is_recursive = 'recursive' in request.args or bool(
                     Config.recur_archive)
                 tar.add(path_real + i, i, recursive=is_recursive)
             tar.close()  # Pointer is at the end of file
             tmp.seek(0)  # Read at start
             response = send_file(tmp,
                                  environ,
                                  mimetype=mimeLib.getmime('*.tar'))
         elif command == 'files.lst':
             uni_param.interpreter = self.itp_filelist
             path_real_dir = request.path_real_dir + '/'
             shown_files = [
                 x for x in os.listdir(request.path_real_dir)
                 if x[0:1] != '.'
             ] if Config.hide_dots else os.listdir(request.path_real_dir)
             paths = [join_path(path_real_dir, x) for x in shown_files]
             items = [ItemEntry(x, x, path_real_dir) for x in paths]
             filelist = FileList(items)
             uni_param.filelist = filelist
             page = uni_param.interpreter.get_page('', uni_param)
             response = Response(page.content,
                                 page.status,
                                 page.headers,
                                 mimetype=mimeLib.getmime('*.txt'))
         return self.return_response(request, response, environ,
                                     start_response)
     elif resource != None:
         # Filelist or send file or 404
         if os.path.exists(resource):
             if os.path.isdir(resource):
                 # List files
                 if 'no list' not in uni_param.interpreter.sections[
                         ''].params:
                     path_real_dir = request.path_real_dir + '/'
                     shown_files = [
                         x for x in os.listdir(path_real_dir)
                         if x[0:1] != '.'
                     ] if Config.hide_dots else os.listdir(path_real_dir)
                     paths = [
                         join_path(path_real_dir, x) for x in shown_files
                     ]
                     items = [ItemEntry(x, x, path_real_dir) for x in paths]
                     filelist = FileList(items)
                     uni_param.filelist = filelist
                 page = uni_param.interpreter.get_page('', uni_param)
                 response = Response(page.content,
                                     page.status,
                                     page.headers,
                                     mimetype=mimeLib.getmime(
                                         '*.txt' if uni_param.interpreter ==
                                         self.itp_filelist else '*.html'))
             elif levels_real[-1].lower().endswith(
                     '.zip') and Config.preview_zip:
                 # Preview zip file if configured
                 if resource not in self.cached_zip_files:
                     try:
                         zip_file = zipfile.ZipFile(resource, 'r')
                     except zipfile.BadZipFile:
                         response = Response(
                             I18n.get_string('zip_file_is_broken'), 202)
                         return self.return_response(
                             request, response, environ, start_response)
                     self.cached_zip_files[resource] = (zip_file, )
                 zip_file_data = self.cached_zip_files[resource]
                 if 'getitem' in request.args:
                     zip_file = zip_file_data[0]
                     filename = request.args['getitem']
                     try:
                         response = Response(
                             zip_file.read(filename),
                             200,
                             mimetype=mimeLib.getmime(filename))
                     except KeyError:
                         response = self.not_found_response(request)
                 else:
                     items = [
                         ZipItemEntry(x, resource, path)
                         for x in zip_file_data[0].filelist
                         if x.filename[-1:] != '/'
                     ]
                     filelist = FileList(items)
                     uni_param.filelist = filelist
                     page = uni_param.interpreter.get_page('', uni_param)
                     response = Response(
                         page.content,
                         page.status,
                         page.headers,
                         mimetype=mimeLib.getmime(
                             '*.txt' if uni_param.interpreter ==
                             self.itp_filelist else '*.html'))
             else:
                 # A file
                 response = send_file(resource, environ)
         else:
             # 404
             response = self.not_found_response(request)
         return self.return_response(request, response, environ,
                                     start_response)
     else:
         response = self.not_found_response(request)
     return self.return_response(request, response, environ, start_response)
Пример #12
0
def test_disposition_name(as_attachment, value):
    rv = send_file(txt_path, environ, as_attachment=as_attachment)
    assert rv.headers["Content-Disposition"] == f"{value}; filename=test.txt"
    rv.close()
Пример #13
0
def test_text_mode_fails(file_factory):
    with file_factory() as f, pytest.raises(ValueError, match="binary mode"):
        send_file(f, environ, mimetype="text/plain")
Пример #14
0
def test_object_mimetype_from_name():
    rv = send_file(io.BytesIO(b"test"), environ, download_name="test.txt")
    assert rv.mimetype == "text/plain"
    rv.close()
Пример #15
0
def test_object_without_mimetype():
    with pytest.raises(TypeError, match="detect the MIME type"):
        send_file(io.BytesIO(b"test"), environ)
Пример #16
0
def test_content_encoding():
    rv = send_file(txt_path, environ, download_name="logo.svgz")
    rv.close()
    assert rv.mimetype == "image/svg+xml"
    assert rv.content_encoding == "gzip"
Пример #17
0
def test_last_modified():
    last_modified = datetime.datetime(1999, 1, 1)
    rv = send_file(txt_path, environ, last_modified=last_modified)
    assert rv.last_modified == last_modified
    rv.close()
Пример #18
0
def test_x_sendfile():
    rv = send_file(html_path, environ, use_x_sendfile=True)
    assert rv.headers["x-sendfile"] == str(html_path)
    assert rv.data == b""
    rv.close()
Пример #19
0
def test_max_age_callable():
    # This is a private API, it should only be used by Flask.
    rv = send_file(txt_path, environ, max_age=lambda p: 10)
    rv.close()
    assert rv.cache_control.max_age == 10