~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to src/apps/server/__init__.py

  • Committer: mattgiuca
  • Date: 2007-12-19 23:28:03 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:89
Moved safe exec/interpreter code out of apps/server/__init__.py into
common/interpret.py, as it will be needed by several apps.

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
# for python files, we evaluate the python script inside
25
25
# our safe execution environment.
26
26
 
27
 
from common import (util, studpath)
 
27
from common import (util, studpath, interpret)
28
28
import conf
29
29
import conf.app.server
30
30
 
31
 
import functools
32
31
import mimetypes
33
 
import os
34
 
import pwd
35
 
import subprocess
36
32
 
37
33
def handle(req):
38
34
    """Handler for the Server application which serves pages."""
70
66
        interp_name = conf.app.server.interpreters[type]
71
67
        try:
72
68
            # Get the interpreter function object
73
 
            interp_object = interpreter_objects[interp_name]
 
69
            interp_object = interpret.interpreter_objects[interp_name]
74
70
        except KeyError:
75
71
            # TODO: Nicer 500 message (this is due to bad configuration in
76
72
            # conf/app/server.py)
77
73
            req.throw_error(req.HTTP_INTERNAL_SERVER_ERROR)
78
 
        interpret_file(req, owner, filename, interp_object)
 
74
        interpret.interpret_file(req, owner, filename, interp_object)
79
75
 
80
76
    else:
81
77
        # Otherwise, use the blacklist/whitelist to see if this file should be
103
99
        req.throw_error(req.HTTP_NOT_FOUND)
104
100
    req.content_type = type
105
101
    req.sendfile(filename)
106
 
 
107
 
def interpret_file(req, owner, filename, interpreter):
108
 
    """Serves a file by interpreting it using one of IVLE's builtin
109
 
    interpreters. All interpreters are intended to run in the user's jail. The
110
 
    jail location is provided as an argument to the interpreter but it is up
111
 
    to the individual interpreters to create the jail.
112
 
 
113
 
    req: An IVLE request object.
114
 
    owner: Username of the user who owns the file being served.
115
 
    filename: Filename in the local file system.
116
 
    interpreter: A function object to call.
117
 
    """
118
 
    # Make sure the file exists (otherwise some interpreters may not actually
119
 
    # complain).
120
 
    # Don't test for execute permission, that will only be required for
121
 
    # certain interpreters.
122
 
    if not os.access(filename, os.R_OK):
123
 
        req.throw_error(req.HTTP_NOT_FOUND)
124
 
 
125
 
    # Get the UID of the owner of the file
126
 
    # (Note: files are executed by their owners, not the logged in user.
127
 
    # This ensures users are responsible for their own programs and also
128
 
    # allows them to be executed by the public).
129
 
    try:
130
 
        (_,_,uid,_,_,_,_) = pwd.getpwnam(owner)
131
 
    except KeyError:
132
 
        # The user does not exist. This should have already failed the
133
 
        # previous test.
134
 
        req.throw_error(req.HTTP_INTERNAL_SERVER_ERROR)
135
 
 
136
 
    # Split up req.path again, this time with respect to the jail
137
 
    (_, jail_dir, path) = studpath.url_to_jailpaths(req.path)
138
 
    path = os.path.join('/', path)
139
 
    (working_dir, _) = os.path.split(path)
140
 
    # Now jail_dir is the jail directory relative to the jails root.
141
 
    # Note that the trampoline has jails root hard-coded for security.
142
 
    # path is the filename relative to the user's jail.
143
 
    # working_dir is the directory containing the file relative to the user's
144
 
    # jail.
145
 
    # (Note that paths "relative" to the jail actually begin with a '/' as
146
 
    # they are absolute in the jailspace)
147
 
 
148
 
    return interpreter(uid, jail_dir, working_dir, path, req)
149
 
 
150
 
# Used to store mutable data
151
 
class Dummy:
152
 
    pass
153
 
 
154
 
def execute_cgi(trampoline, uid, jail_dir, working_dir, script_path, req):
155
 
    """
156
 
    trampoline: Full path on the local system to the CGI wrapper program
157
 
        being executed.
158
 
    uid: User ID of the owner of the file.
159
 
    jail_dir: Owner's jail directory relative to the jails root.
160
 
    working_dir: Directory containing the script file relative to owner's
161
 
        jail.
162
 
    script_path: CGI script relative to the owner's jail.
163
 
    req: IVLE request object.
164
 
 
165
 
    The called CGI wrapper application shall be called using popen and receive
166
 
    the HTTP body on stdin. It shall receive the CGI environment variables to
167
 
    its environment.
168
 
    """
169
 
 
170
 
    # Get the student program's directory and execute it from that context.
171
 
    (tramp_dir, _) = os.path.split(trampoline)
172
 
 
173
 
    # TODO: Don't create a file if the body length is known to be 0
174
 
    # Write the HTTP body to a temporary file so it can be passed as a *real*
175
 
    # file to popen.
176
 
    f = os.tmpfile()
177
 
    body = req.read()
178
 
    if body is not None:
179
 
        f.write(body)
180
 
        f.flush()
181
 
        f.seek(0)       # Rewind, for reading
182
 
 
183
 
    # usage: tramp uid jail_dir working_dir script_path
184
 
    pid = subprocess.Popen(
185
 
        [trampoline, str(uid), jail_dir, working_dir, script_path],
186
 
        stdin=f, stdout=subprocess.PIPE, cwd=tramp_dir)
187
 
 
188
 
    # process_cgi_line: Reads a single line of CGI output and processes it.
189
 
    # Prints to req, and also does fancy HTML warnings if Content-Type
190
 
    # omitted.
191
 
    cgiflags = Dummy()
192
 
    cgiflags.got_cgi_header = False
193
 
    cgiflags.started_cgi_body = False
194
 
    cgiflags.wrote_html_warning = False
195
 
    def process_cgi_line(line):
196
 
        # FIXME? Issue with binary files (processing per-line?)
197
 
        if cgiflags.started_cgi_body:
198
 
            # FIXME: HTML escape text if wrote_html_warning
199
 
            req.write(line)
200
 
        else:
201
 
            # Read CGI headers
202
 
            if line.strip() == "" and cgiflags.got_cgi_header:
203
 
                cgiflags.started_cgi_body = True
204
 
            elif line.startswith("Content-Type:"):
205
 
                req.content_type = line[13:].strip()
206
 
                cgiflags.got_cgi_header = True
207
 
            elif line.startswith("Location:"):
208
 
                # TODO
209
 
                cgiflags.got_cgi_header = True
210
 
            elif line.startswith("Status:"):
211
 
                # TODO
212
 
                cgiflags.got_cgi_header = True
213
 
            elif cgiflags.got_cgi_header:
214
 
                # Invalid header
215
 
                # TODO
216
 
                req.write("Invalid header")
217
 
                pass
218
 
            else:
219
 
                # Assume the user is not printing headers and give a warning
220
 
                # about that.
221
 
                # User program did not print header.
222
 
                # Make a fancy HTML warning for them.
223
 
                req.content_type = "text/html"
224
 
                req.write("""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
225
 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
226
 
<html xmlns="http://www.w3.org/1999/xhtml">
227
 
<head>
228
 
  <meta http-equiv="Content-Type"
229
 
    content="text/html; charset=utf-8" />
230
 
</head>
231
 
<body style="margin: 0; padding: 0; font-family: sans;">
232
 
  <div style="background-color: #faa; border-bottom: 1px solid black;
233
 
    padding: 8px;">
234
 
    <p><strong>Warning</strong>: You did not print a "Content-Type" header.
235
 
    CGI requires you to print some content type. You may wish to try:</p>
236
 
    <pre style="margin-left: 1em">Content-Type: text/html</pre>
237
 
  </div>
238
 
  <div style="margin: 8px;">
239
 
    <pre>
240
 
""")
241
 
                cgiflags.got_cgi_header = True
242
 
                cgiflags.wrote_html_warning = True
243
 
                cgiflags.started_cgi_body = True
244
 
                req.write(line)
245
 
 
246
 
    # Read from the process's stdout into req
247
 
    for line in pid.stdout:
248
 
        process_cgi_line(line)
249
 
 
250
 
    # If we wrote an HTML warning header, write the footer
251
 
    if cgiflags.wrote_html_warning:
252
 
        req.write("""</pre>
253
 
  </div>
254
 
</body>
255
 
</html>""")
256
 
 
257
 
# TODO: Replace mytest with cgi trampoline handler script
258
 
location_cgi_python = os.path.join(conf.ivlepath, "bin/trampoline-python")
259
 
 
260
 
# Mapping of interpreter names (as given in conf/app/server.py) to
261
 
# interpreter functions.
262
 
 
263
 
interpreter_objects = {
264
 
    'cgi-python'
265
 
        : functools.partial(execute_cgi, location_cgi_python),
266
 
    # Should also have:
267
 
    # cgi-generic
268
 
    # python-server-page
269
 
}
270