Completed
Push — master ( 890bd3...b13734 )
by Felipe A.
45s
created

browsepy.open_file()   A

Complexity

Conditions 3

Size

Total Lines 9

Duplication

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