Exemple #1
0
 def test_dump_options_header(self):
     assert http.dump_options_header("foo", {"bar": 42}) == "foo; bar=42"
     assert http.dump_options_header("foo", {
         "bar": 42,
         "fizz": None
     }) in (
         "foo; bar=42; fizz",
         "foo; fizz; bar=42",
     )
Exemple #2
0
 def _set_charset(self, charset):
     header = self.headers.get('content-type')
     ct, options = parse_options_header(header)
     if not ct:
         raise TypeError('Cannot set charset if Content-Type header is missing.')
     options['charset'] = charset
     self.headers['Content-Type'] = dump_options_header(ct, options)
Exemple #3
0
 def _set_charset(self, charset):
     header = self.headers.get("content-type")
     ct, options = parse_options_header(header)
     if not ct:
         raise TypeError("Cannot set charset if Content-Type " "header is missing.")
     options["charset"] = charset
     self.headers["Content-Type"] = dump_options_header(ct, options)
 def _set_charset(self, charset):
     header = self.headers.get('content-type')
     ct, options = parse_options_header(header)
     if not ct:
         raise TypeError('Cannot set charset if Content-Type header is missing.')
     options['charset'] = charset
     self.headers['Content-Type'] = dump_options_header(ct, options)
Exemple #5
0
 def _set_charset(self, charset):
     header = self.headers.get("content-type")
     ct, options = parse_options_header(header)
     if not ct:
         raise TypeError("Cannot set charset if Content-Type "
                         "header is missing.")
     options["charset"] = charset
     self.headers["Content-Type"] = dump_options_header(ct, options)
Exemple #6
0
 def _set_charset(self, charset):
     # noinspection PyUnresolvedReferences
     header = self.headers.get('content-type')
     ct, options = parse_options_header(header)
     if not ct:
         raise TypeError('Cannot set charset if Content-Type '
                         'header is missing.')
     options['charset'] = charset
     self.headers['Content-Type'] = dump_options_header(ct, options)
Exemple #7
0
 def _set_charset(self, charset):
     warnings.warn(
         "'werkzeug.contrib.wrappers.DynamicCharsetResponseMixin'"
         " is deprecated as of version 0.15 and will be removed in"
         " version 1.0.",
         DeprecationWarning,
         stacklevel=2,
     )
     header = self.headers.get('content-type')
     ct, options = parse_options_header(header)
     if not ct:
         raise TypeError('Cannot set charset if Content-Type '
                         'header is missing.')
     options['charset'] = charset
     self.headers['Content-Type'] = dump_options_header(ct, options)
 def _set_charset(self, charset):
     warnings.warn(
         "'werkzeug.contrib.wrappers.DynamicCharsetResponseMixin'"
         " is deprecated as of version 0.15 and will be removed in"
         " version 1.0.",
         DeprecationWarning,
         stacklevel=2,
     )
     header = self.headers.get('content-type')
     ct, options = parse_options_header(header)
     if not ct:
         raise TypeError('Cannot set charset if Content-Type '
                         'header is missing.')
     options['charset'] = charset
     self.headers['Content-Type'] = dump_options_header(ct, options)
Exemple #9
0
 def on_update(d):
     self.headers["Content-Type"] = dump_options_header(
         self.mimetype, d)
Exemple #10
0
 def on_update(d: CallbackDict) -> None:
     self.headers["Content-Type"] = dump_options_header(self.mimetype, d)
def _options_header_vkw(value, kw):
    if not kw:
        return value
    return dump_options_header(value, dict(((k.replace('_', '-'), v) for k, v in kw.items())))
Exemple #12
0
 def test_dump_options_header(self):
     assert http.dump_options_header('foo', {'bar': 42}) == \
         'foo; bar=42'
     assert http.dump_options_header('foo', {'bar': 42, 'fizz': None}) in \
         ('foo; bar=42; fizz', 'foo; fizz; bar=42')
Exemple #13
0
 def _on_update(value: Dict[str, Any]) -> None:
     self.headers["Content-Type"] = dump_options_header(
         self.mimetype, value)
Exemple #14
0
    def handle_request(filepath=''):
        """Handle an HTTP request (HEAD, GET, POST).
        """
        # replace SCRIPT_NAME with the custom if set
        if config['app']['base']:
            request.environ['SCRIPT_NAME'] = config['app']['base']

        query = request.values

        action = query.get('a', default='view')
        action = query.get('action', default=action)

        format = query.get('f')
        format = query.get('format', default=format)

        # handle authorization
        auth_result = handle_authorization(action=action, format=format)
        if auth_result is not None:
            return auth_result

        # determine primary variables
        #
        # filepath: the URL path below app base (not percent encoded)
        # localpath: the file system path corresponding to filepath
        # localtargetpath: localpath with symbolic link resolved
        # mimetype: the mimetype from localtargetpath
        # archivefile: the file system path of the ZIP archive file, or None
        # subarchivepath: the URL path below archivefile (not percent encoded)
        localpath = os.path.abspath(
            os.path.join(runtime['root'], filepath.strip('/\\')))
        localtargetpath = os.path.realpath(localpath)
        archivefile, subarchivepath = get_archive_path(filepath, localpath)
        mimetype, _ = mimetypes.guess_type(localtargetpath)

        # handle action
        if action == 'static':
            if format:
                return http_error(400, "Action not supported.", format=format)

            for i in runtime['statics']:
                f = os.path.join(i, filepath)
                if os.path.lexists(f):
                    return static_file(filepath, root=i)
            else:
                return http_error(404)

        elif action == 'source':
            if format:
                return http_error(400, "Action not supported.", format=format)

            if archivefile:
                response = handle_subarchive_path(
                    os.path.realpath(archivefile),
                    subarchivepath,
                    mimetype,
                    list_directory=False)
            else:
                response = static_file(filepath,
                                       root=runtime['root'],
                                       mimetype=mimetype)

            # show as inline plain text
            # @TODO: Chromium (80) seems to ignore header mimetype for certain types
            #        like image and zip
            encoding = query.get('e', 'utf-8')
            encoding = query.get('encoding', default=encoding)
            response.headers.set('Content-Type',
                                 'text/plain; charset=' + quote(encoding))
            response.headers.set('Content-Disposition', 'inline')

            return response

        elif action in ('exec', 'browse'):
            if not is_local_access():
                return http_error(400,
                                  "Command can only run on local device.",
                                  format=format)

            if not os.path.lexists(localpath):
                return http_error(404, "File does not exist.", format=format)

            if action == 'browse':
                util.view_in_explorer(localpath)

            elif action == 'exec':
                util.launch(localpath)

            if format:
                return http_response('Command run successfully.',
                                     format=format)

            return http_response(status=204)

        elif action == 'token':
            return http_response(token_handler.acquire(), format=format)

        elif action == 'list':
            if not format:
                return http_error(400, "Action not supported.", format=format)

            if os.path.isdir(localpath):
                recursive = query.get('recursive', type=bool)
                return handle_directory_listing(localtargetpath,
                                                recursive=recursive,
                                                format=format)

            return http_error(400, "This is not a directory.", format=format)

        elif action == 'config':
            if not format:
                return http_error(400, "Action not supported.", format=format)

            data = config.dump_object()

            # filter values for better security
            data = {k: v for k, v in data.items() if k in ('app', 'book')}
            data['app'] = {
                k: v
                for k, v in data['app'].items() if k in ('name', 'theme')
            }

            # add and rewrite values for client to better know the server
            data['app']['base'] = request.script_root
            data['app']['is_local'] = is_local_access()
            data['VERSION'] = __version__
            data['WSB_DIR'] = WSB_DIR
            data['WSB_LOCAL_CONFIG'] = WSB_LOCAL_CONFIG

            return http_response(data, format=format)

        elif action == 'edit':
            if format:
                return http_error(400, "Action not supported.", format=format)

            if os.path.lexists(localpath) and not os.path.isfile(localpath):
                return http_error(400, "Found a non-file here.", format=format)

            if archivefile:
                with zipfile.ZipFile(archivefile, 'r') as zip:
                    try:
                        info = zip.getinfo(subarchivepath)
                    except:
                        body = b''
                    else:
                        body = zip.read(info)
            else:
                try:
                    with open(localpath, 'rb') as f:
                        body = f.read()
                        f.close()
                except FileNotFoundError:
                    body = b''

            encoding = query.get('e')
            encoding = query.get('encoding', default=encoding)

            try:
                body = body.decode(encoding or 'UTF-8')
            except (LookupError, UnicodeDecodeError):
                encoding = 'ISO-8859-1'
                body = body.decode(encoding)

            body = render_template(
                'edit.html',
                sitename=runtime['name'],
                is_local=is_local_access(),
                base=request.script_root,
                path=request.path,
                body=body,
                encoding=encoding,
            )

            return http_response(body, format=format)

        elif action == 'editx':
            if format:
                return http_error(400, "Action not supported.", format=format)

            if os.path.lexists(localpath) and not os.path.isfile(localpath):
                return http_error(400, "Found a non-file here.", format=format)

            if not mimetype in ("text/html", "application/xhtml+xml"):
                return http_error(400,
                                  "This is not an HTML file.",
                                  format=format)

            if archivefile:
                with zipfile.ZipFile(archivefile, 'r') as zip:
                    try:
                        info = zip.getinfo(subarchivepath)
                    except:
                        return http_error(404,
                                          "File does not exist.",
                                          format=format)
            else:
                if not os.path.lexists(localpath):
                    return http_error(404,
                                      "File does not exist.",
                                      format=format)

            body = render_template(
                'editx.html',
                sitename=runtime['name'],
                is_local=is_local_access(),
                base=request.script_root,
                path=request.path,
            )

            return http_response(body, format=format)

        elif action in ('lock', 'unlock', 'mkdir', 'save', 'delete', 'move',
                        'copy'):
            if request.method != 'POST':
                headers = {
                    'Allow': 'POST',
                }
                return http_error(405,
                                  'Method "{}" not allowed.'.format(
                                      request.method),
                                  format=format,
                                  headers=headers)

            # validate and revoke token
            token = query.get('token') or ''

            if not token_handler.validate(token):
                return http_error(400, 'Invalid access token.', format=format)

            token_handler.delete(token)

            # validate localpath
            if action not in ('lock', 'unlock'):
                if os.path.abspath(localpath) == runtime['root']:
                    return http_error(403,
                                      "Unable to operate the root directory.",
                                      format=format)

            # validate targetpath
            if action in ('lock', 'unlock'):
                name = query.get('name')
                if name is None:
                    return http_error(400,
                                      "Lock name is not specified.",
                                      format=format)

                targetpath = os.path.join(runtime['locks'], name)
                if not targetpath.startswith(os.path.join(
                        runtime['locks'], '')):
                    return http_error(400,
                                      'Invalid lock name "{}".'.format(name),
                                      format=format)

            # handle action

            # action lock
            # name: name of the lock file.
            # chkt: recheck until the lock file not exist or fail out when time out.
            # chks: how long to treat the lock file as stale.
            if action == 'lock':
                check_stale = query.get('chks', 300, type=int)
                check_timeout = query.get('chkt', 5, type=int)
                check_expire = time.time() + check_timeout
                check_delta = min(check_timeout, 0.1)

                while True:
                    try:
                        os.makedirs(targetpath)
                    except FileExistsError:
                        t = time.time()

                        if t >= check_expire or not os.path.isdir(targetpath):
                            return http_error(
                                500,
                                'Unable to acquire lock "{}".'.format(name),
                                format=format)

                        try:
                            lock_expire = os.stat(
                                targetpath).st_mtime + check_stale
                        except FileNotFoundError:
                            # Lock removed by another process during the short interval.
                            # Try acquire again.
                            continue

                        if t >= lock_expire:
                            # Lock expired. Touch rather than remove and make for atomicity.
                            try:
                                Path(targetpath).touch()
                            except:
                                traceback.print_exc()
                                return http_error(
                                    500,
                                    'Unable to regenerate stale lock "{}".'.
                                    format(name),
                                    format=format)
                            else:
                                break

                        time.sleep(check_delta)
                    except:
                        traceback.print_exc()
                        return http_error(
                            500,
                            'Unable to create lock "{}".'.format(name),
                            format=format)
                    else:
                        break

            elif action == 'unlock':
                try:
                    os.rmdir(targetpath)
                except FileNotFoundError:
                    pass
                except:
                    traceback.print_exc()
                    return http_error(
                        500,
                        'Unable to remove lock "{}".'.format(name),
                        format=format)

            elif action == 'mkdir':
                if os.path.lexists(localpath) and not os.path.isdir(localpath):
                    return http_error(400,
                                      "Found a non-directory here.",
                                      format=format)

                if archivefile:
                    try:
                        zip = zipfile.ZipFile(archivefile, 'a')
                        subarchivepath = subarchivepath + '/'

                        try:
                            info = zip.getinfo(subarchivepath)
                        except KeyError:
                            # subarchivepath does not exist
                            info = zipfile.ZipInfo(subarchivepath,
                                                   time.localtime())
                            zip.writestr(info,
                                         b'',
                                         compress_type=zipfile.ZIP_STORED)
                    except:
                        traceback.print_exc()
                        return http_error(500,
                                          "Unable to write to this ZIP file.",
                                          format=format)

                else:
                    try:
                        os.makedirs(localpath, exist_ok=True)
                    except OSError:
                        traceback.print_exc()
                        return http_error(500,
                                          "Unable to create a directory here.",
                                          format=format)

            elif action == 'save':
                if os.path.lexists(
                        localpath) and not os.path.isfile(localpath):
                    return http_error(400,
                                      "Found a non-file here.",
                                      format=format)

                if archivefile:
                    try:
                        zip0 = zip = zipfile.ZipFile(archivefile, 'a')

                        try:
                            # if subarchivepath exists, open a new zip file for writing.
                            try:
                                info = zip.getinfo(subarchivepath)
                            except KeyError:
                                info = zipfile.ZipInfo(subarchivepath,
                                                       time.localtime())
                            else:
                                info.date_time = time.localtime()
                                temp_path = archivefile + '.' + str(time_ns())
                                zip = zipfile.ZipFile(temp_path, 'w')

                            try:
                                # write to the zip file
                                file = request.files.get('upload')
                                if file is not None:
                                    with zip.open(info, 'w',
                                                  force_zip64=True) as fp:
                                        stream = file.stream
                                        while True:
                                            s = stream.read(8192)
                                            if not s: break
                                            fp.write(s)
                                        fp.close()
                                else:
                                    bytes = query.get('text',
                                                      '').encode('ISO-8859-1')
                                    zip.writestr(
                                        info,
                                        bytes,
                                        compress_type=zipfile.ZIP_DEFLATED,
                                        compresslevel=9)

                                # copy zip0 content to zip
                                if zip is not zip0:
                                    for info in zip0.infolist():
                                        if info.filename == subarchivepath:
                                            continue
                                        zip.writestr(
                                            info,
                                            zip0.read(info),
                                            compress_type=info.compress_type,
                                            compresslevel=None
                                            if info.compress_type
                                            == zipfile.ZIP_STORED else 9)
                            except:
                                # remove the generated zip file if writing fails
                                if zip is not zip0:
                                    zip.close()
                                    os.remove(zip.filename)

                                raise
                            else:
                                # replace zip0 with the generated zip file
                                if zip is not zip0:
                                    zip0.close()
                                    zip.close()

                                    temp_path = archivefile + '.' + str(
                                        time_ns() + 1)
                                    os.rename(archivefile, temp_path)
                                    os.rename(zip.filename, archivefile)
                                    os.remove(temp_path)
                        finally:
                            zip0.close()
                    except:
                        traceback.print_exc()
                        return http_error(500,
                                          "Unable to write to this ZIP file.",
                                          format=format)

                else:
                    try:
                        os.makedirs(os.path.dirname(localpath), exist_ok=True)
                    except:
                        traceback.print_exc()
                        return http_error(500,
                                          "Unable to write to this path.",
                                          format=format)

                    try:
                        file = request.files.get('upload')
                        if file is not None:
                            file.save(localpath)
                        else:
                            bytes = query.get('text', '').encode('ISO-8859-1')
                            with open(localpath, 'wb') as f:
                                f.write(bytes)
                                f.close()
                    except:
                        traceback.print_exc()
                        return http_error(500,
                                          "Unable to write to this file.",
                                          format=format)

            elif action == 'delete':
                if archivefile:
                    try:
                        zip0 = zipfile.ZipFile(archivefile, 'r')
                        temp_path = archivefile + '.' + str(time_ns())
                        zip = zipfile.ZipFile(temp_path, 'w')

                        try:
                            deleted = False
                            for info in zip0.infolist():
                                if (info.filename == subarchivepath
                                        or info.filename.startswith(
                                            subarchivepath + '/')):
                                    deleted = True
                                    continue

                                zip.writestr(
                                    info,
                                    zip0.read(info),
                                    compress_type=info.compress_type,
                                    compresslevel=None if info.compress_type
                                    == zipfile.ZIP_STORED else 9)
                        except:
                            # remove the generated zip file if writing fails
                            zip.close()
                            os.remove(zip.filename)

                            raise
                        else:
                            zip0.close()
                            zip.close()

                            if not deleted:
                                os.remove(zip.filename)
                                return http_error(
                                    404,
                                    "Entry does not exist in this ZIP file.",
                                    format=format)

                            # replace zip0 with the generated zip file
                            temp_path = archivefile + '.' + str(time_ns() + 1)
                            os.rename(archivefile, temp_path)
                            os.rename(zip.filename, archivefile)
                            os.remove(temp_path)
                    except:
                        traceback.print_exc()
                        return http_error(500,
                                          "Unable to write to this ZIP file.",
                                          format=format)

                else:
                    if not os.path.lexists(localpath):
                        return http_error(404,
                                          "File does not exist.",
                                          format=format)

                    if os.path.islink(localpath):
                        try:
                            os.remove(localpath)
                        except:
                            traceback.print_exc()
                            return http_error(500,
                                              "Unable to delete this link.",
                                              format=format)
                    elif os.path.isfile(localpath):
                        try:
                            os.remove(localpath)
                        except:
                            traceback.print_exc()
                            return http_error(500,
                                              "Unable to delete this file.",
                                              format=format)
                    elif os.path.isdir(localpath):
                        try:
                            try:
                                # try rmdir for a possible windows directory junction,
                                # which is not detected by os.path.islink
                                os.rmdir(localpath)
                            except OSError:
                                # directory not empty
                                shutil.rmtree(localpath)
                        except:
                            traceback.print_exc()
                            return http_error(
                                500,
                                "Unable to delete this directory.",
                                format=format)

            elif action == 'move':
                if archivefile:
                    return http_error(400,
                                      "File is inside an archive file.",
                                      format=format)

                if not os.path.lexists(localpath):
                    return http_error(404,
                                      "File does not exist.",
                                      format=format)

                target = query.get('target')

                if target is None:
                    return http_error(400,
                                      'Target is not specified.',
                                      format=format)

                targetpath = os.path.normpath(
                    os.path.join(runtime['root'], target.strip('/')))

                if not targetpath.startswith(os.path.join(runtime['root'],
                                                          '')):
                    return http_error(
                        403,
                        "Unable to operate beyond the root directory.",
                        format=format)

                if os.path.lexists(targetpath):
                    return http_error(
                        400,
                        'Found something at target "{}".'.format(target),
                        format=format)

                ta, tsa = get_archive_path(target, targetpath)
                if ta:
                    return http_error(400,
                                      "Move target is inside an archive file.",
                                      format=format)

                os.makedirs(os.path.dirname(targetpath), exist_ok=True)

                try:
                    os.rename(localpath, targetpath)
                except:
                    traceback.print_exc()
                    return http_error(
                        500,
                        'Unable to move to target "{}".'.format(target),
                        format=format)

            elif action == 'copy':
                if archivefile:
                    return http_error(400,
                                      "File is inside an archive file.",
                                      format=format)

                if not os.path.lexists(localpath):
                    return http_error(404,
                                      "File does not exist.",
                                      format=format)

                target = query.get('target')

                if target is None:
                    return http_error(400,
                                      'Target is not specified.',
                                      format=format)

                targetpath = os.path.normpath(
                    os.path.join(runtime['root'], target.strip('/')))

                if not targetpath.startswith(os.path.join(runtime['root'],
                                                          '')):
                    return http_error(
                        403,
                        "Unable to operate beyond the root directory.",
                        format=format)

                if os.path.lexists(targetpath):
                    return http_error(
                        400,
                        'Found something at target "{}".'.format(target),
                        format=format)

                ta, tsa = get_archive_path(target, targetpath)
                if ta:
                    return http_error(400,
                                      "Copy target is inside an archive file.",
                                      format=format)

                os.makedirs(os.path.dirname(targetpath), exist_ok=True)

                try:
                    try:
                        shutil.copytree(localpath, targetpath)
                    except NotADirectoryError:
                        shutil.copy2(localpath, targetpath)
                except:
                    traceback.print_exc()
                    return http_error(
                        500,
                        'Unable to copy to target "{}".'.format(target),
                        format=format)

            if format:
                return http_response('Command run successfully.',
                                     format=format)

            return http_response(status=204)

        # "view" or undefined actions
        elif action == 'view':
            # show file information for other output formats
            if format:
                info = util.file_info(localpath)
                data = {
                    'name': info.name,
                    'type': info.type,
                    'size': info.size,
                    'last_modified': info.last_modified,
                    'mime': mimetype,
                }
                return http_response(data, format=format)

            # handle directory
            if os.path.isdir(localpath):
                return handle_directory_listing(localtargetpath)

            # handle file
            elif os.path.isfile(localpath):
                # view archive file
                if mimetype in ("application/html+zip", "application/x-maff"):
                    return handle_archive_viewing(localtargetpath, mimetype)

                # view markdown
                if mimetype == "text/markdown":
                    return handle_markdown_output(filepath, localtargetpath)

                # convert meta refresh to 302 redirect
                if localtargetpath.lower().endswith('.htm'):
                    target = util.parse_meta_refresh(localtargetpath).target

                    if target is not None:
                        # Keep several chars as javascript encodeURI do,
                        # plus "%" as target may have already been escaped.
                        new_url = urljoin(
                            request.url, quote(target,
                                               ";,/?:@&=+$-_.!~*'()#%"))
                        return redirect(new_url)

                # show static file for other cases
                response = static_file(filepath,
                                       root=runtime['root'],
                                       mimetype=mimetype)

            # handle sub-archive path
            elif archivefile:
                response = handle_subarchive_path(
                    os.path.realpath(archivefile), subarchivepath, mimetype)

            else:
                return http_error(404)

            # don't include charset
            m, p = parse_options_header(response.headers.get('Content-Type'))
            try:
                del p['charset']
            except KeyError:
                pass
            response.headers.set('Content-Type', dump_options_header(m, p))

            return response

        # unknown action
        else:
            return http_error(400, "Action not supported.", format=format)
Exemple #15
0
def _options_header_vkw(value, kw):
    if not kw:
        return value
    return dump_options_header(value, dict(((k.replace('_', '-'), v) for k, v in kw.items())))
Exemple #16
0
 def on_update(d):
     self.headers['Content-Type'] = \
         dump_options_header(self.mimetype, d)
 def on_update(d):
     self.headers['Content-Type'] = dump_options_header(self.mimetype, d)
Exemple #18
0
 def test_dump_options_header(self):
     assert http.dump_options_header("foo", {"bar": 42}) == "foo; bar=42"
     assert http.dump_options_header("foo", {"bar": 42, "fizz": None}) in ("foo; bar=42; fizz", "foo; fizz; bar=42")
Exemple #19
0
 def test_dump_options_header(self):
     assert http.dump_options_header('foo', {'bar': 42}) == \
         'foo; bar=42'
     assert http.dump_options_header('foo', {'bar': 42, 'fizz': None}) in \
         ('foo; bar=42; fizz', 'foo; fizz; bar=42')
Exemple #20
0
 def on_update(d: t.Dict[str, str]) -> None:
     self.headers["Content-Type"] = dump_options_header(self.mimetype, d)