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

« back to all changes in this revision

Viewing changes to ivle/interpret.py

  • Committer: William Grant
  • Date: 2010-07-03 08:18:50 UTC
  • Revision ID: grantw@unimelb.edu.au-20100703081850-ygl8vp5nyznf3dnk
Trailing slashes in URLs can now be detected by views accepting subpaths.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
import pwd
32
32
import subprocess
33
33
import cgi
34
 
import StringIO
35
34
 
36
35
# TODO: Make progressive output work
37
36
# Question: Will having a large buffer size stop progressive output from
40
39
CGI_BLOCK_SIZE = 65535
41
40
PATH = "/usr/local/bin:/usr/bin:/bin"
42
41
 
43
 
def interpret_file(req, owner, jail_dir, filename, interpreter, gentle=True,
44
 
    overrides=None):
 
42
def interpret_file(req, owner, jail_dir, filename, interpreter, gentle=True):
45
43
    """Serves a file by interpreting it using one of IVLE's builtin
46
44
    interpreters. All interpreters are intended to run in the user's jail. The
47
45
    jail location is provided as an argument to the interpreter but it is up
52
50
    jail_dir: Absolute path to the user's jail.
53
51
    filename: Absolute filename within the user's jail.
54
52
    interpreter: A function object to call.
55
 
    gentle: ?
56
 
    overrides: A dict mapping env var names to strings, to override arbitrary
57
 
        environment variables in the resulting CGI environent.
58
53
    """
59
54
    # We can't test here whether or not the target file actually exists,
60
55
    # because the apache user may not have permission. Instead we have to
80
75
    # they are absolute in the jailspace)
81
76
 
82
77
    return interpreter(owner, jail_dir, working_dir, filename_abs, req,
83
 
                       gentle, overrides=overrides)
 
78
                       gentle)
84
79
 
85
80
class CGIFlags:
86
81
    """Stores flags regarding the state of reading CGI output.
95
90
        self.headers = {}       # Header names : values
96
91
 
97
92
def execute_cgi(interpreter, owner, jail_dir, working_dir, script_path,
98
 
                req, gentle, overrides=None):
 
93
                req, gentle):
99
94
    """
100
95
    trampoline: Full path on the local system to the CGI wrapper program
101
96
        being executed.
105
100
        jail.
106
101
    script_path: CGI script relative to the owner's jail.
107
102
    req: IVLE request object.
108
 
    gentle: ?
109
 
    overrides: A dict mapping env var names to strings, to override arbitrary
110
 
        environment variables in the resulting CGI environent.
111
103
 
112
104
    The called CGI wrapper application shall be called using popen and receive
113
105
    the HTTP body on stdin. It shall receive the CGI environment variables to
138
130
        f.seek(0)       # Rewind, for reading
139
131
 
140
132
    # Set up the environment
141
 
    environ = cgi_environ(req, script_path, owner, overrides=overrides)
 
133
    environ = cgi_environ(req, script_path, owner)
142
134
 
143
135
    # usage: tramp uid jail_dir working_dir script_path
144
136
    cmd_line = [trampoline, str(owner.unixid),
294
286
        process_cgi_output(req, line + '\n', cgiflags)
295
287
        return
296
288
 
297
 
    # Check if CGI field-name is valid
298
 
    CGI_SEPERATORS = set(['(', ')', '<', '>', '@', ',', ';', ':', '\\', '"',
299
 
            '/', '[', ']', '?', '=', '{', '}', ' ', '\t'])
300
 
    if any((char in CGI_SEPERATORS for char in name)):
301
 
        warning = "Warning"
302
 
        if not cgiflags.gentle:
303
 
            message = """An unexpected server error has occured."""
304
 
            warning = "Error"
305
 
        else:
306
 
            # Header contained illegal characters
307
 
            message = """You printed an invalid CGI header. CGI header
308
 
            field-names can not contain any of the following characters: 
309
 
            <code>( ) &lt; &gt; @ , ; : \\ " / [ ] ? = { } <em>SPACE 
310
 
            TAB</em></code>."""
311
 
        write_html_warning(req, message, warning=warning)
312
 
        cgiflags.wrote_html_warning = True
313
 
        # Handle the rest of this line as normal data
314
 
        process_cgi_output(req, line + '\n', cgiflags)
315
 
        return
316
 
 
317
289
    # Read CGI headers
318
290
    value = value.strip()
319
291
    if name == "Content-Type":
376
348
    # python-server-page
377
349
}
378
350
 
379
 
def cgi_environ(req, script_path, user, overrides=None):
 
351
def cgi_environ(req, script_path, user):
380
352
    """Gets CGI variables from apache and makes a few changes for security and 
381
353
    correctness.
382
354
 
383
355
    Does not modify req, only reads it.
384
 
 
385
 
    overrides: A dict mapping env var names to strings, to override arbitrary
386
 
        environment variables in the resulting CGI environent.
387
356
    """
388
357
    env = {}
389
358
    # Comments here are on the heavy side, explained carefully for security
444
413
    username = user.login
445
414
    env['HOME'] = os.path.join('/home', username)
446
415
 
447
 
    if overrides is not None:
448
 
        env.update(overrides)
449
416
    return env
450
417
 
451
418
class ExecutionError(Exception):
484
451
        raise ExecutionError('subprocess ended with code %d, stderr: "%s"' %
485
452
                             (exitcode, stderr))
486
453
    return (stdout, stderr)
487
 
 
488
 
def jail_call(req, cgi_script, script_name, query_string=None,
489
 
    request_method="GET", extra_overrides=None):
490
 
    """
491
 
    Makes a call to a CGI script inside the jail from outside the jail.
492
 
    This can be used to allow Python scripts to access jail-only functions and
493
 
    data without having to perform a full API request.
494
 
 
495
 
    req: A Request object (will not be written to or attributes modified).
496
 
    cgi_script: Path to cgi script outside of jail.
497
 
        eg: os.path.join(req.config['paths']['share'],
498
 
                         'services/fileservice')
499
 
    script_name: Name to set as SCRIPT_NAME for the CGI environment.
500
 
        eg: "/fileservice/"
501
 
    query_string: Query string to set as QUERY_STRING for the CGI environment.
502
 
        eg: "action=svnrepostat&path=/users/studenta/"
503
 
    request_method: Method to set as REQUEST_METHOD for the CGI environment.
504
 
        eg: "POST". Defaults to "GET".
505
 
    extra_overrides: A dict mapping env var names to strings, to override
506
 
        arbitrary environment variables in the resulting CGI environent.
507
 
 
508
 
    Returns a triple (status_code, content_type, contents).
509
 
    """
510
 
    interp_object = interpreter_objects["cgi-python"]
511
 
    user_jail_dir = os.path.join(req.config['paths']['jails']['mounts'],
512
 
                                 req.user.login)
513
 
    overrides = {
514
 
        "SCRIPT_NAME": script_name,
515
 
        "QUERY_STRING": query_string,
516
 
        "REQUEST_URI": "%s%s%s" % (script_name, "?" if query_string else "",
517
 
                                   query_string),
518
 
        "REQUEST_METHOD": request_method,
519
 
    }
520
 
    if extra_overrides is not None:
521
 
        overrides.update(extra_overrides)
522
 
    result = DummyReq(req)
523
 
    interpret_file(result, req.user, user_jail_dir, cgi_script, interp_object,
524
 
                   gentle=False, overrides=overrides)
525
 
    return result.status, result.content_type, result.getvalue()
526
 
 
527
 
class DummyReq(StringIO.StringIO):
528
 
    """A dummy request object, built from a real request object, which can be
529
 
    used like a req but doesn't mutate the existing request.
530
 
    (Used for reading CGI responses as strings rather than forwarding their
531
 
    output to the current request.)
532
 
    """
533
 
    def __init__(self, req):
534
 
        StringIO.StringIO.__init__(self)
535
 
        self._real_req = req
536
 
    def get_cgi_environ(self):
537
 
        return self._real_req.get_cgi_environ()
538
 
    def __getattr__(self, name):
539
 
        return getattr(self._real_req, name)