Completed
Push — dev-4.1 ( bc345b...dbd54d )
by Felipe A.
01:15
created

page_not_found_error()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
1
#!/usr/bin/env python
2
# -*- coding: UTF-8 -*-
3
4
import logging
5
import os
6
import os.path
7
8
from flask import Flask, Response, request, render_template, redirect, \
9
                  url_for, send_from_directory, stream_with_context, \
10
                  make_response
11
from werkzeug.exceptions import NotFound
12
13
from .__meta__ import __app__, __version__, __license__, __author__  # noqa
14
from .manager import PluginManager
15
from .file import Node, OutsideRemovableBase, OutsideDirectoryBase, \
16
                  secure_filename
17
from . import compat
18
19
__basedir__ = os.path.abspath(os.path.dirname(compat.fsdecode(__file__)))
20
21
logger = logging.getLogger(__name__)
22
23
app = Flask(
24
    __name__,
25
    static_url_path='/static',
26
    static_folder=os.path.join(__basedir__, "static"),
27
    template_folder=os.path.join(__basedir__, "templates")
28
    )
29
app.config.update(
30
    directory_base=compat.getcwd(),
31
    directory_start=compat.getcwd(),
32
    directory_remove=None,
33
    directory_upload=None,
34
    directory_tar_buffsize=262144,
35
    directory_downloadable=True,
36
    use_binary_multiples=True,
37
    plugin_modules=[],
38
    plugin_namespaces=(
39
        'browsepy.plugin',
40
        '',
41
        ),
42
    )
43
44
if "BROWSEPY_SETTINGS" in os.environ:
45
    app.config.from_envvar("BROWSEPY_SETTINGS")
46
47
plugin_manager = PluginManager(app)
48
49
50
def stream_template(template_name, **context):
51
    '''
52
    Some templates can be huge, this function returns an streaming response,
53
    sending the content in chunks and preventing from timeout.
54
55
    :param template_name: template
56
    :param **context: parameters for templates.
57
    :yields: HTML strings
58
    '''
59
    app.update_template_context(context)
60
    template = app.jinja_env.get_template(template_name)
61
    stream = template.generate(context)
62
    return Response(stream_with_context(stream))
63
64
65
@app.context_processor
66
def template_globals():
67
    return {
68
        'manager': app.extensions['plugin_manager'],
69
        'len': len,
70
        }
71
72
73
@app.route("/browse", defaults={"path": ""})
74
@app.route('/browse/<path:path>')
75
def browse(path):
76
    try:
77
        directory = Node.from_urlpath(path)
78
        if directory.is_directory:
79
            return stream_template("browse.html", file=directory)
80
    except OutsideDirectoryBase:
81
        pass
82
    return NotFound()
83
84
85
@app.route('/open/<path:path>', endpoint="open")
86
def open_file(path):
87
    try:
88
        file = Node.from_urlpath(path)
89
        if file.is_file:
90
            return send_from_directory(file.parent.path, file.name)
91
    except OutsideDirectoryBase:
92
        pass
93
    return NotFound()
94
95
96
@app.route("/download/file/<path:path>")
97
def download_file(path):
98
    try:
99
        file = Node.from_urlpath(path)
100
        if file.is_file:
101
            return file.download()
102
    except OutsideDirectoryBase:
103
        pass
104
    return NotFound()
105
106
107
@app.route("/download/directory/<path:path>.tgz")
108
def download_directory(path):
109
    try:
110
        directory = Node.from_urlpath(path)
111
        if directory.is_directory:
112
            return directory.download()
113
    except OutsideDirectoryBase:
114
        pass
115
    return NotFound()
116
117
118
@app.route("/remove/<path:path>", methods=("GET", "POST"))
119
def remove(path):
120
    try:
121
        file = Node.from_urlpath(path)
122
    except OutsideDirectoryBase:
123
        return NotFound()
124
    if request.method == 'GET':
125
        if not file.can_remove:
126
            return NotFound()
127
        return render_template('remove.html', file=file)
128
    parent = file.parent
129
    if parent is None:
130
        # base is not removable
131
        return NotFound()
132
133
    try:
134
        file.remove()
135
    except OutsideRemovableBase:
136
        return NotFound()
137
138
    return redirect(url_for(".browse", path=parent.urlpath))
139
140
141
@app.route("/upload", defaults={'path': ''}, methods=("POST",))
142
@app.route("/upload/<path:path>", methods=("POST",))
143
def upload(path):
144
    try:
145
        directory = Node.from_urlpath(path)
146
    except OutsideDirectoryBase:
147
        return NotFound()
148
149
    if not directory.is_directory or not directory.can_upload:
150
        return NotFound()
151
152
    for f in request.files.values():
153
        filename = secure_filename(f.filename)
154
        if filename:
155
            filename = directory.choose_filename(filename)
156
            filepath = os.path.join(directory.path, filename)
157
            f.save(filepath)
158
    return redirect(url_for(".browse", path=directory.urlpath))
159
160
161
@app.route("/")
162
def index():
163
    path = app.config["directory_start"] or app.config["directory_base"]
164
    try:
165
        urlpath = Node(path).urlpath
166
    except OutsideDirectoryBase:
167
        return NotFound()
168
    return browse(urlpath)
169
170
171
@app.after_request
172
def page_not_found(response):
173
    if response.status_code == 404:
174
        return make_response((render_template('404.html'), 404))
175
    return response
176
177
178
@app.errorhandler(404)
179
def page_not_found_error(e):
180
    return render_template('404.html'), 404
181
182
183
@app.errorhandler(500)
184
def internal_server_error(e):  # pragma: no cover
185
    logger.exception(e)
186
    return getattr(e, 'message', 'Internal server error'), 500
187