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

« back to all changes in this revision

Viewing changes to lib/common/interpret.py

  • Committer: mattgiuca
  • Date: 2008-02-05 01:41:15 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:409
Moved www/conf and www/common to a new directory lib. This separates the "web"
part of IVLE from what is becoming less web oriented (at least from Apache's
standpoint).
Modified setup.py to install this lib directory correctly and write conf in
the right place. Also adds the lib directory to ivle.pth.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
# This can be resolved but needs careful sanitisation. See fixup_environ.
29
29
 
30
30
from common import studpath
31
 
from common import db
32
 
from common.util import IVLEError, IVLEJailError
33
31
import conf
34
32
import functools
35
33
 
44
42
 
45
43
CGI_BLOCK_SIZE = 65535
46
44
 
47
 
uids = {}
48
 
 
49
 
def get_uid(login):
50
 
    """Get the unix uid corresponding to the given login name.
51
 
       If it is not in the dictionary of uids, then consult the
52
 
       database and retrieve an update of the user table."""
53
 
    global uids
54
 
    if login in uids:
55
 
        return uids[login]
56
 
 
57
 
    conn = db.DB()
58
 
    res = conn.get_all('login', ['login', 'unixid'])
59
 
    def repack(flds):
60
 
        return (flds['login'], flds['unixid'])
61
 
    uids = dict(map(repack,res))
62
 
 
63
 
    return uids[login]
64
 
 
65
 
def interpret_file(req, owner, jail_dir, filename, interpreter, gentle=True):
 
45
def interpret_file(req, owner, filename, interpreter):
66
46
    """Serves a file by interpreting it using one of IVLE's builtin
67
47
    interpreters. All interpreters are intended to run in the user's jail. The
68
48
    jail location is provided as an argument to the interpreter but it is up
70
50
 
71
51
    req: An IVLE request object.
72
52
    owner: Username of the user who owns the file being served.
73
 
    jail_dir: Absolute path to the user's jail.
74
 
    filename: Absolute filename within the user's jail.
 
53
    filename: Filename in the local file system.
75
54
    interpreter: A function object to call.
76
55
    """
77
 
    # We can't test here whether or not the target file actually exists,
78
 
    # because the apache user may not have permission. Instead we have to
79
 
    # rely on the interpreter generating an error.
80
 
    if filename.startswith(os.sep):
81
 
        filename_abs = filename
82
 
        filename_rel = filename[1:]
83
 
    else:
84
 
        filename_abs = os.path.join(os.sep, filename)
85
 
        filename_rel = filename
 
56
    # Make sure the file exists (otherwise some interpreters may not actually
 
57
    # complain).
 
58
    # Don't test for execute permission, that will only be required for
 
59
    # certain interpreters.
 
60
    if not os.access(filename, os.R_OK):
 
61
        req.throw_error(req.HTTP_NOT_FOUND)
86
62
 
87
63
    # Get the UID of the owner of the file
88
64
    # (Note: files are executed by their owners, not the logged in user.
89
65
    # This ensures users are responsible for their own programs and also
90
66
    # allows them to be executed by the public).
91
 
    uid = get_uid(owner)
 
67
    try:
 
68
        (_,_,uid,_,_,_,_) = pwd.getpwnam(owner)
 
69
    except KeyError:
 
70
        # The user does not exist. This should have already failed the
 
71
        # previous test.
 
72
        req.throw_error(req.HTTP_INTERNAL_SERVER_ERROR)
92
73
 
93
74
    # Split up req.path again, this time with respect to the jail
94
 
    (working_dir, _) = os.path.split(filename_abs)
 
75
    (_, jail_dir, path) = studpath.url_to_jailpaths(req.path)
 
76
    path = os.path.join('/', path)
 
77
    (working_dir, _) = os.path.split(path)
95
78
    # jail_dir is the absolute jail directory.
96
79
    # path is the filename relative to the user's jail.
97
80
    # working_dir is the directory containing the file relative to the user's
99
82
    # (Note that paths "relative" to the jail actually begin with a '/' as
100
83
    # they are absolute in the jailspace)
101
84
 
102
 
    return interpreter(uid, jail_dir, working_dir, filename_abs, req,
103
 
                       gentle)
 
85
    return interpreter(uid, jail_dir, working_dir, path, req)
104
86
 
105
87
class CGIFlags:
106
 
    """Stores flags regarding the state of reading CGI output.
107
 
       If this is to be gentle, detection of invalid headers will result in an
108
 
       HTML warning."""
109
 
    def __init__(self, begentle=True):
110
 
        self.gentle = begentle
 
88
    """Stores flags regarding the state of reading CGI output."""
 
89
    def __init__(self):
111
90
        self.started_cgi_body = False
112
91
        self.got_cgi_headers = False
113
92
        self.wrote_html_warning = False
115
94
        self.headers = {}       # Header names : values
116
95
 
117
96
def execute_cgi(interpreter, trampoline, uid, jail_dir, working_dir,
118
 
                script_path, req, gentle):
 
97
                script_path, req):
119
98
    """
120
99
    trampoline: Full path on the local system to the CGI wrapper program
121
100
        being executed.
170
149
    # process_cgi_line: Reads a single line of CGI output and processes it.
171
150
    # Prints to req, and also does fancy HTML warnings if Content-Type
172
151
    # omitted.
173
 
    cgiflags = CGIFlags(gentle)
 
152
    cgiflags = CGIFlags()
174
153
 
175
154
    # Read from the process's stdout into req
176
155
    data = pid.stdout.read(CGI_BLOCK_SIZE)
202
181
        # Break data into lines of CGI header data. 
203
182
        linebuf = cgiflags.linebuf + data
204
183
        # First see if we can split all header data
205
 
        # We need to get the double CRLF- or LF-terminated headers, whichever
206
 
        # is smaller, as either sequence may appear somewhere in the body.
207
 
        usplit = linebuf.split('\n\n', 1)
208
 
        wsplit = linebuf.split('\r\n\r\n', 1)
209
 
        split = len(usplit[0]) > len(wsplit[0]) and wsplit or usplit
 
184
        split = linebuf.split('\r\n\r\n', 1)
 
185
        if len(split) == 1:
 
186
            # Allow UNIX newlines instead
 
187
            split = linebuf.split('\n\n', 1)
210
188
        if len(split) == 1:
211
189
            # Haven't seen all headers yet. Buffer and come back later.
212
190
            cgiflags.linebuf = linebuf
232
210
            if len(split) == 1:
233
211
                split = headers.split('\n', 1)
234
212
 
235
 
        # Is this an internal IVLE error condition?
236
 
        hs = cgiflags.headers
237
 
        if 'X-IVLE-Error-Type' in hs:
238
 
            t = hs['X-IVLE-Error-Type']
239
 
            if t == IVLEError.__name__:
240
 
                raise IVLEError(int(hs['X-IVLE-Error-Code']),
241
 
                                hs['X-IVLE-Error-Message'])
242
 
            else:
243
 
                try:
244
 
                    raise IVLEJailError(hs['X-IVLE-Error-Type'],
245
 
                                        hs['X-IVLE-Error-Message'],
246
 
                                        hs['X-IVLE-Error-Info'])
247
 
                except KeyError:
248
 
                    raise IVLEError(500, 'bad error headers written by CGI')
249
 
 
250
213
        # Check to make sure the required headers were written
251
 
        if cgiflags.wrote_html_warning or not cgiflags.gentle:
 
214
        if cgiflags.wrote_html_warning:
252
215
            # We already reported an error, that's enough
253
216
            pass
254
217
        elif "Content-Type" in cgiflags.headers:
282
245
        name, value = line.split(':', 1)
283
246
    except ValueError:
284
247
        # No colon. The user did not write valid headers.
285
 
        # If we are being gentle, we want to help the user understand what
286
 
        # went wrong. Otherwise, just admit we screwed up.
287
 
        warning = "Warning"
288
 
        if not cgiflags.gentle:
289
 
            message = """An unexpected server error has occured."""
290
 
            warning = "Error"
291
 
        elif len(cgiflags.headers) == 0:
 
248
        if len(cgiflags.headers) == 0:
292
249
            # First line was not a header line. We can assume this is not
293
250
            # a CGI app.
294
251
            message = """You did not print a CGI header.
299
256
            # header.
300
257
            message = """You printed an invalid CGI header. You need to leave
301
258
a blank line after the headers, before writing the page contents."""
302
 
        write_html_warning(req, message, warning=warning)
 
259
        write_html_warning(req, message)
303
260
        cgiflags.wrote_html_warning = True
304
261
        # Handle the rest of this line as normal data
305
262
        process_cgi_output(req, line + '\n', cgiflags)
318
275
        try:
319
276
            req.status = int(value.split(' ', 1)[0])
320
277
        except ValueError:
321
 
            if not cgiflags.gentle:
322
 
                # This isn't user code, so it should be good.
323
 
                # Get us out of here!
324
 
                raise
325
278
            message = """The "Status" CGI header was invalid. You need to
326
279
print a number followed by a message, such as "302 Found"."""
327
280
            write_html_warning(req, message)
334
287
        req.headers_out[name] = value
335
288
    cgiflags.headers[name] = value
336
289
 
337
 
def write_html_warning(req, text, warning="Warning"):
 
290
def write_html_warning(req, text):
338
291
    """Prints an HTML warning about invalid CGI interaction on the part of the
339
292
    user. text may contain HTML markup."""
340
293
    req.content_type = "text/html"
348
301
<body style="margin: 0; padding: 0; font-family: sans-serif;">
349
302
  <div style="background-color: #faa; border-bottom: 1px solid black;
350
303
    padding: 8px;">
351
 
    <p><strong>%s</strong>: %s
 
304
    <p><strong>Warning</strong>: %s
352
305
  </div>
353
306
  <div style="margin: 8px;">
354
307
    <pre>
355
 
""" % (warning, text))
 
308
""" % text)
356
309
 
357
310
location_cgi_python = os.path.join(conf.ivle_install_dir,
358
311
    "bin/trampoline")
379
332
    # Comments here are on the heavy side, explained carefully for security
380
333
    # reasons. Please read carefully before making changes.
381
334
 
 
335
    # Remove HTTP_COOKIE. It is a security risk to have students see the IVLE
 
336
    # cookie of their visitors.
 
337
    try:
 
338
        del env['HTTP_COOKIE']
 
339
    except: pass
 
340
 
382
341
    # Remove DOCUMENT_ROOT and SCRIPT_FILENAME. Not part of CGI spec and
383
342
    # exposes unnecessary details about server.
384
343
    try: