Source

trac-ticketlinks / trac / ModPythonHandler.py

Full commit
# -*- coding: iso8859-1 -*-
#
# Copyright (C) 2004 Edgewall Software
# Copyright (C) 2004 Christopher Lenz <cmlenz@gmx.de>
#
# Trac is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# Trac is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Author: Christopher Lenz <cmlenz@gmx.de>

import os
import re, threading
import auth, core, Environment, Href, Wiki
from util import TracError, href_join
from mod_python import apache, util

class ModPythonRequest(core.Request):

    def __init__(self, req):
        self.req = req

    def init_request(self):
        core.Request.init_request(self)
        options = self.req.get_options()

        if options.has_key('TracEnvParentDir') and self.req.path_info:
            # We have to remove one path element from path_info when we're
            # using TracEnvParentDir
            self.path_info = re.sub('/[^/]+', '', self.req.path_info, 1)
        else:
            self.path_info = self.req.path_info
            
        if len(self.path_info):
            self.cgi_location = self.req.uri[:-len(self.path_info)]
        else:
            self.cgi_location = self.req.uri

        if len(self.req.path_info):
            self.idx_location = self.req.uri[:-len(self.req.path_info)]
        else:
            self.idx_location = self.req.uri

        # TODO This will need proxy host name support (see #437 and [581])
        host = self.req.hostname
        port = self.req.connection.local_addr[1]
        if port == 80:
            self.base_url = 'http://%s%s' % (host, self.cgi_location)
        elif port == 443:
            self.base_url = 'https://%s%s' % (host, self.cgi_location)
        else:
            self.base_url = 'http://%s:%d%s' % (host, port, self.cgi_location)

        self.remote_addr = self.req.connection.remote_ip
        self.remote_user = self.req.user
        self.command = self.req.method

        if self.req.headers_in.has_key('Cookie'):
            self.incookie.load(self.req.headers_in['Cookie'])

        self.hdf.setValue('HTTP.Host', self.req.hostname)

    def read(self, len):
        return self.req.read(len)

    def write(self, data):
        self.req.write(data)

    def get_header(self, name):
        return self.req.headers_in.get(name)

    def send_response(self, code):
        self.req.status = code

    def send_header(self, name, value):
        if name.lower() == 'content-type':
            self.req.content_type = value
        else:
            self.req.headers_out.add(name, str(value))

    def end_headers(self):
        pass


class TracFieldStorage(util.FieldStorage):
    """
    FieldStorage class with an added get function.
    """
    def get(self, key, default=''):
        return util.FieldStorage.get(self, key, default)


def send_project_index(req, mpr, dir):
    req.content_type = 'text/html'
    req.write('<html><head><title>Available Projects</title></head>')
    req.write('<body><h1>Available Projects</h1><ul>')
    for project in os.listdir(dir):
        req.write('<li><a href="%s">%s</a></li>' % (href_join(mpr.idx_location,
                                                              project),
                                                    project))
    req.write('</ul></body><html>')

def open_environment(env_path, mpr):
    env = Environment.Environment(env_path)
    version = env.get_version()
    if version < Environment.db_version:
        raise TracError('The Trac environment needs to be upgraded. '
                        'Run "trac-admin %s upgrade"' % env_path)
    elif version > Environment.db_version:
        raise TracError('Unknown Trac Environment version (%d).' % version)

    env.href = Href.Href(mpr.cgi_location)
    env.abs_href = Href.Href(mpr.base_url)
    # Let the wiki module build a dictionary of all page names
    database = env.get_db_cnx()
    Wiki.populate_page_dict(database, env)
    return env

env_cache = {}
env_cache_lock = threading.Lock()

def get_environment(req, mpr):
    global env_cache, env_cache_lock
    options = req.get_options()
    
    if not options.has_key('TracEnv') and not options.has_key('TracEnvParentDir'):
        raise EnvironmentError, \
              'Missing PythonOption "TracEnv" or "TracEnvParentDir". Trac '\
              'requires one of these options to locate the Trac environment(s).'
    
    if options.has_key('TracEnv'):
        env_path = options['TracEnv']
        
    elif options.has_key('TracEnvParentDir'):
        env_parent_dir = options['TracEnvParentDir']
        env_name = mpr.cgi_location.split('/')[-1]
        env_path = os.path.join(env_parent_dir, env_name)
        if len(env_name) == 0 or not os.path.exists(env_path):
            send_project_index(req, mpr, env_parent_dir)
            return None
        
    env_cache_lock.acquire()
    if not env_path in env_cache:
        env_cache[env_path] = open_environment(env_path, mpr)
    env = env_cache[env_path]
    env_cache_lock.release()
    return env

def handler(req):
    global projects, projects_lock

    mpr = ModPythonRequest(req)
    mpr.init_request()

    env = get_environment(req, mpr)
    if not env:
        return apache.OK

    args = TracFieldStorage(req)
    core.parse_path_info(args, mpr.path_info)

    req.content_type = 'text/html'
    try:
        core.dispatch_request(mpr.path_info, args, mpr, env)
    except Exception, e:
        core.send_pretty_error(e, env, mpr)
    return apache.OK