1354 lines
51 KiB
Python
1354 lines
51 KiB
Python
# -*- coding: iso-8859-15 -*-
|
|
|
|
|
|
# Glasnost
|
|
# By: Odile Bénassy <obenassy@entrouvert.com>
|
|
# Romain Chantereau <rchantereau@entrouvert.com>
|
|
# Nicolas Clapiès <nclapies@easter-eggs.org>
|
|
# Pierre-Antoine Dejace <padejace@entrouvert.be>
|
|
# Thierry Dulieu <tdulieu@easter-eggs.com>
|
|
# Florent Monnier <monnier@codelutin.com>
|
|
# Cédric Musso <cmusso@easter-eggs.org>
|
|
# Frédéric Péters <fpeters@entrouvert.be>
|
|
# Benjamin Poussin <poussin@codelutin.com>
|
|
# Emmanuel Raviart <eraviart@entrouvert.com>
|
|
# Sébastien Régnier <regnier@codelutin.com>
|
|
# Emmanuel Saracco <esaracco@easter-eggs.com>
|
|
#
|
|
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
|
|
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
|
|
# Emmanuel Saracco & Théridion
|
|
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
|
|
# Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
|
|
# Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
|
|
# Régnier, Emmanuel Saracco, Théridion & Vecam
|
|
#
|
|
# This program 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.
|
|
#
|
|
# This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
__doc__ = """Glasnost Web Handler"""
|
|
|
|
__version__ = '$Revision$'[11:-2]
|
|
|
|
|
|
import __builtin__
|
|
import base64
|
|
import binascii
|
|
import Cookie
|
|
import cStringIO
|
|
import errno
|
|
import fcntl
|
|
import imp
|
|
import locale
|
|
import md5
|
|
import os
|
|
import socket
|
|
import sys
|
|
import time
|
|
import types
|
|
|
|
try:
|
|
import gzip
|
|
except ImportError:
|
|
gzip = None
|
|
|
|
from mod_python import apache, util
|
|
|
|
glasnostPythonDir = '/usr/local/lib/glasnost-devel' # changed on make install
|
|
sys.path.insert(0, glasnostPythonDir)
|
|
|
|
import glasnost
|
|
|
|
import glasnost.common.applications as applications
|
|
import glasnost.common.context as context
|
|
from glasnost.common.cache import cache
|
|
import glasnost.common.faults as faults
|
|
import glasnost.common.tools_new as commonTools
|
|
import glasnost.web.tools_new as webTools
|
|
import glasnost.common.xhtmlgenerator as X
|
|
|
|
from glasnost.proxy.DispatcherProxy import getRegisteredRoles
|
|
|
|
from glasnost.web.tools import *
|
|
|
|
|
|
class Application(applications.Application):
|
|
applicationName = 'GlasnostWeb'
|
|
applicationRole = 'web'
|
|
|
|
def callFunction(self):
|
|
function = context.getVar('function')
|
|
positionalArguments = context.getVar('positionalArguments')
|
|
keywordsArguments = context.getVar('keywordsArguments')
|
|
objectId = context.getVar('objectId')
|
|
|
|
if positionalArguments is None:
|
|
arguments = []
|
|
else:
|
|
arguments = positionalArguments[:]
|
|
if keywordsArguments is None:
|
|
keywords = {}
|
|
else:
|
|
keywords = keywordsArguments.copy()
|
|
if objectId is not None:
|
|
keywords['id'] = objectId
|
|
|
|
if keywords.has_key('nextUri'):
|
|
context.setVar('nextUri', keywords['nextUri'])
|
|
del keywords['nextUri']
|
|
|
|
functionCode = function.func_code
|
|
expectedArguments = functionCode.co_varnames[:functionCode.co_argcount]
|
|
if function.func_defaults is None:
|
|
requiredArguments = expectedArguments
|
|
else:
|
|
requiredArguments = expectedArguments[
|
|
:-len(function.func_defaults)]
|
|
acceptsArbitraryKeywordsArguments = functionCode.co_flags & 8 != 0
|
|
acceptsArbitraryPositionalArguments = functionCode.co_flags & 4 != 0
|
|
|
|
# Remove unexpected arguments.
|
|
if not acceptsArbitraryKeywordsArguments:
|
|
for argumentName in keywords.keys():
|
|
if argumentName not in expectedArguments:
|
|
del keywords[argumentName]
|
|
|
|
# Insert the needed keywords arguments inside the positional arguments
|
|
# and check that there is no missing argument.
|
|
newArguments = []
|
|
hasSelf = 0
|
|
for argumentName in requiredArguments:
|
|
if argumentName == 'self':
|
|
hasSelf = 1
|
|
elif argumentName in keywords.keys():
|
|
newArguments.append(keywords[argumentName])
|
|
del keywords[argumentName]
|
|
elif not arguments:
|
|
if context.getVar('debug'):
|
|
raise Exception('missing argument = %s' % argumentName)
|
|
return HTTP_NOT_FOUND
|
|
else:
|
|
newArguments.append(arguments[0])
|
|
del arguments[0]
|
|
arguments = newArguments + arguments
|
|
|
|
# Check that there is not too much positional arguments.
|
|
if not acceptsArbitraryPositionalArguments \
|
|
and hasSelf + len(arguments) > len(expectedArguments):
|
|
if context.getVar('debug'):
|
|
raise Exception('Too much positional arguments = %s'
|
|
% arguments)
|
|
return HTTP_NOT_FOUND
|
|
|
|
req = context.getVar('req')
|
|
try:
|
|
try:
|
|
result = function(*arguments, **keywords)
|
|
finally:
|
|
if req.buffered:
|
|
req.finish()
|
|
session = context.getVar('session')
|
|
if session is not None:
|
|
getProxyForServerRole('sessions').setSession(session,
|
|
context.getVar('virtualHost').defaultDispatcherId)
|
|
except (faults.MissingItem, faults.UnknownServerId):
|
|
if context.getVar('debug'):
|
|
raise
|
|
if req.caching:
|
|
req.cancel()
|
|
if req.buffered:
|
|
req.finish()
|
|
return HTTP_NOT_FOUND
|
|
except faults.UserAccessDenied:
|
|
if context.getVar('debug'):
|
|
raise
|
|
if req.caching:
|
|
req.cancel()
|
|
if req.buffered:
|
|
req.finish()
|
|
return HTTP_FORBIDDEN
|
|
except IOError, exc:
|
|
if exc.filename:
|
|
# TODO: check this doesn't catch IOError to client socket
|
|
raise
|
|
# Write failed, client closed connection.
|
|
if req.caching:
|
|
req.cancel()
|
|
return HTTP_INTERNAL_SERVER_ERROR
|
|
except:
|
|
if req.caching:
|
|
req.cancel()
|
|
raise
|
|
|
|
return result
|
|
|
|
def handler(self, req):
|
|
# Use direct access to _req for speed.
|
|
try:
|
|
_req = req._req
|
|
except AttributeError:
|
|
_req = req
|
|
|
|
# If the file at webFilePath exists and is static (images,
|
|
# stylesheets...) then return its content directly.
|
|
if not (_req.filename.endswith('/index') or
|
|
_req.filename.endswith('.py') or
|
|
_req.filename.endswith('.tal') or
|
|
_req.filename.endswith('.xtal')) and \
|
|
os.path.exists(_req.filename):
|
|
staticFile = None
|
|
try:
|
|
staticFile = open(_req.filename)
|
|
except IOError, error:
|
|
if error.errno in (errno.EACCESS, errno.EISDIR):
|
|
return HTTP_FORBIDDEN
|
|
if staticFile:
|
|
lastModTime = time.gmtime(os.stat(_req.filename)[-2])
|
|
return self.outputStaticFile(
|
|
req, staticFile.read(), lastModTime)
|
|
|
|
context.push(
|
|
_level = 'handler',
|
|
applicationId = None,
|
|
applicationToken = None,
|
|
cache = None,
|
|
canUseCookie = 0, # Are the cookies accepted by the client browser?
|
|
cookie = None,
|
|
defaultDispatcherId = None,
|
|
dispatcherId = None,
|
|
fallbackTemplatesDirectoryPath = None,
|
|
function = None,
|
|
htmlHeaders = [],
|
|
httpHostName = None,
|
|
httpLocalPath = None,
|
|
# The HTTP path, without the httpScriptDirectoryPath prefix.
|
|
httpMethod = req.method,
|
|
httpPath = None,
|
|
# The full HTTP path, beginning with httpScriptDirectoryPath.
|
|
httpPort = None,
|
|
httpProtocol = None, # http or https
|
|
httpQuery = None,
|
|
httpScriptDirectoryPath = None,
|
|
# Used when glasnost is inside another web site.
|
|
keywordsArguments = None,
|
|
knownRoles = None,
|
|
languageSetInUrl = 0,
|
|
objectId = None,
|
|
positionalArguments = None,
|
|
preferences = None,
|
|
readLanguages = None,
|
|
req = req,
|
|
sectionLevel = 1,
|
|
serverRole = None,
|
|
session = None,
|
|
sessionToken = None,
|
|
sessionTokenInCookie = 0, # Was the sessionToken stored in the url?
|
|
talEngine = None,
|
|
templateDirectoryName = None,
|
|
templatesDirectoryPath = None,
|
|
templateFileName = None,
|
|
templatePrefix = None,
|
|
userId = '',
|
|
userToken = '',
|
|
virtualHost = None,
|
|
webFileName = None,
|
|
webDirectoryPaths = [],
|
|
xmlPost = None,
|
|
)
|
|
|
|
httpPath = _req.uri
|
|
while len(httpPath) > 1 and httpPath[-1] == '/':
|
|
httpPath = httpPath[:-1]
|
|
if not httpPath:
|
|
httpPath = '/'
|
|
context.setVar('httpPath', httpPath)
|
|
context.setVar('httpQuery', req.args)
|
|
if _req.path_info:
|
|
httpScriptPath = _req.uri[: - len(_req.path_info)]
|
|
else:
|
|
httpScriptPath = _req.uri
|
|
while len(httpScriptPath) > 1 and httpScriptPath[-1] == '/':
|
|
httpScriptPath = httpScriptPath[:-1]
|
|
if not httpScriptPath:
|
|
httpScriptPath = '/'
|
|
httpScriptDirectoryPath = commonTools.makeHttpScriptDirectoryPath(
|
|
httpScriptPath)
|
|
context.setVar(
|
|
'httpScriptDirectoryPath', httpScriptDirectoryPath)
|
|
if httpScriptDirectoryPath == '/':
|
|
httpLocalPath = httpPath
|
|
else:
|
|
httpLocalPath = httpPath[len(httpScriptDirectoryPath):]
|
|
context.setVar('httpLocalPath', httpLocalPath)
|
|
|
|
webFilePath = _req.filename
|
|
while len(webFilePath) > 1 and webFilePath[-2] == '/':
|
|
webFilePath = webFilePath[:-1]
|
|
if not webFilePath:
|
|
webFilePath = '/'
|
|
if os.path.isdir(webFilePath):
|
|
webFilePath += '/'
|
|
webDirectoryPath, webFileName = os.path.split(webFilePath)
|
|
if not webFileName:
|
|
choices = ['index.tal', 'index.py', 'index.html']
|
|
for c in choices:
|
|
if os.path.exists(os.path.join(webDirectoryPath, c)):
|
|
webFileName = c
|
|
webFilePath += '/' + webFileName
|
|
break
|
|
else:
|
|
return HTTP_NOT_FOUND
|
|
|
|
context.getVar('webDirectoryPaths').append(webDirectoryPath)
|
|
context.setVar('webFileName', webFileName)
|
|
|
|
webFileExtension = ''
|
|
splittedWebFileName = webFileName.split('.')
|
|
if len(splittedWebFileName) > 1:
|
|
webFileExtension = splittedWebFileName[-1]
|
|
|
|
cookie = None
|
|
cookieContent = {}
|
|
if req.headers_in.has_key('Cookie'):
|
|
cookie = Cookie.SimpleCookie(req.headers_in['Cookie'])
|
|
for k, v in cookie.items():
|
|
cookieContent[k] = v.value
|
|
context.setVar('cookie', cookie)
|
|
|
|
# There seems to be no way to know if we are currently using the http
|
|
# or https protocol!
|
|
# context.setVar('httpProtocol', None)
|
|
httpHostName = None
|
|
httpPort = None
|
|
if req.headers_in.has_key('Host'):
|
|
hostNameAndPort = req.headers_in['Host']
|
|
if ':' in hostNameAndPort:
|
|
httpHostName, httpPort = hostNameAndPort.split(':')
|
|
else:
|
|
httpHostName = hostNameAndPort
|
|
context.setVar('httpHostName', httpHostName)
|
|
context.setVar('httpPort', httpPort)
|
|
|
|
if not httpHostName:
|
|
return HTTP_BAD_REQUEST
|
|
|
|
dispatcherId = 'glasnost://%s' % httpHostName
|
|
# Ensure that dispatcherId is valid.
|
|
dispatcherId = commonTools.extractDispatcherId(dispatcherId)
|
|
context.setVar('dispatcherId', dispatcherId)
|
|
context.setVar('defaultDispatcherId', dispatcherId)
|
|
context.setVar('applicationId', commonTools.makeApplicationId(
|
|
dispatcherId, self.applicationRole))
|
|
|
|
# we have to do it before asking for the VirtualHost object
|
|
context.setVar('cache', cache)
|
|
cache.checkCachedValues()
|
|
|
|
virtualHost = None
|
|
virtualHostsWeb = getWebForServerRole('virtualhosts')
|
|
try:
|
|
virtualHost = virtualHostsWeb.getObjectByHostName(httpHostName)
|
|
except faults.MissingItem:
|
|
if context.getVar('debug'):
|
|
raise 'No Virtual Host defined for %s' % httpHostName
|
|
return HTTP_NOT_FOUND
|
|
except (faults.UnknownDispatcherInId, faults.UnknownServerId):
|
|
if context.getVar('debug'):
|
|
raise
|
|
return HTTP_SERVICE_UNAVAILABLE
|
|
context.setVar('virtualHost', virtualHost)
|
|
|
|
self.loadWebConfigOptions()
|
|
dispatcherId = context.getVar('dispatcherId')
|
|
|
|
if not webFileExtension in ['tal', 'py', 'xtal'] \
|
|
and webFileName != 'index':
|
|
mimeTypes = { '.css': 'text/css',
|
|
'.jpeg': 'image/jpeg',
|
|
'.png': 'image/png' }
|
|
mimeType = None
|
|
for k, v in mimeTypes.items():
|
|
if httpPath.endswith(k):
|
|
mimeType = v
|
|
break
|
|
for path in context.getVar('webDirectoryPaths'):
|
|
fileName = os.path.join(path, httpPath[1:])
|
|
if os.path.exists(fileName):
|
|
staticFilePath = fileName
|
|
try:
|
|
staticFile = open(staticFilePath)
|
|
except IOError, error:
|
|
if error.errno in (errno.EACCESS, errno.EISDIR):
|
|
return HTTP_FORBIDDEN
|
|
lastModTime = time.gmtime(os.stat(staticFilePath)[-2])
|
|
return self.outputStaticFile(
|
|
req, staticFile.read(), lastModTime, mimeType)
|
|
|
|
args = {}
|
|
context.setVar('keywordsArguments', args)
|
|
|
|
# Process input, if any.
|
|
try:
|
|
fieldStorage = util.FieldStorage(req, keep_blank_values = 1)
|
|
except apache.SERVER_RETURN, e:
|
|
if e.args[0] != 501:
|
|
raise
|
|
# POST method with unknown content-type
|
|
ctype = _req.headers_in['content-type']
|
|
if ctype != 'text/xml':
|
|
raise apache.SERVER_RETURN, apache.HTTP_NOT_IMPLEMENTED
|
|
# we support content-type: text/xml for things like the Comment API
|
|
# (see http://wellformedweb.org/story/9)
|
|
clen = int(_req.headers_in['content-length'])
|
|
xmlPostRaw = req.read(clen)
|
|
try:
|
|
import xml.dom.minidom
|
|
except ImportError:
|
|
raise apache.SERVER_RETURN, apache.HTTP_NOT_IMPLEMENTED
|
|
try:
|
|
xmlPost = xml.dom.minidom.parseString(xmlPost)
|
|
except: # TODO: tighter check
|
|
if context.getVar('debug'):
|
|
raise
|
|
raise apache.SERVER_RETURN, apache.HTTP_NOT_ACCEPTABLE
|
|
context.setVar('xmlPost', xmlPost)
|
|
context.setVar('xmlPostRaw', xmlPostRaw)
|
|
class FakeFieldStorage:
|
|
list = []
|
|
fieldStorage = FakeFieldStorage
|
|
|
|
fields = {}
|
|
for field in fieldStorage.list:
|
|
if field.name is not None and not fields.has_key(field.name):
|
|
fields[field.name] = fieldStorage[field.name]
|
|
elif field.name is not None:
|
|
if not type(fields[field.name]) is type([]):
|
|
fields[field.name] = [ fields[field.name] ]
|
|
fields[field.name].append(fieldStorage[field.name])
|
|
|
|
fieldNamesToDelete = []
|
|
for fieldName, fieldValue in fields.items():
|
|
if fieldName[-7:] == '|base64' \
|
|
and type(fieldValue) in [types.StringType, types.UnicodeType]:
|
|
fieldNamesToDelete.append(fieldName)
|
|
name = fieldName[:-7]
|
|
fields[name] = base64.decodestring(fieldValue)
|
|
for fieldName, fieldValue in fields.items():
|
|
if fieldName[-6:] == '_field':
|
|
if type(fieldValue) in [types.StringType, types.UnicodeType]:
|
|
raise Exception('Wrong field value for %s = "%s"' % (
|
|
fieldName, fieldValue))
|
|
else:
|
|
fileContent = fieldValue.file.read()
|
|
if fileContent:
|
|
fieldNamesToDelete.append(fieldName)
|
|
name = fieldName[:-6]
|
|
fields[name] = fileContent
|
|
fields[name + 'FileName'] = fieldValue.filename
|
|
fields[name + 'Type'] = fieldValue.type
|
|
|
|
for fieldName in fieldNamesToDelete:
|
|
del fields[fieldName]
|
|
args.update(fields)
|
|
|
|
# Handle optional session.
|
|
canUseCookie = 1
|
|
sessionToken = None
|
|
sessionTokenInCookie = 0
|
|
sessionTokenInUrl = 0
|
|
if args.has_key('sessionToken'):
|
|
sessionToken = args['sessionToken']
|
|
sessionTokenInUrl = 1
|
|
if cookieContent.has_key('sessionToken'):
|
|
if sessionToken is None:
|
|
sessionTokenInCookie = 1
|
|
sessionToken = cookieContent['sessionToken']
|
|
elif sessionToken == cookieContent['sessionToken']:
|
|
sessionTokenInCookie = 1
|
|
session = None
|
|
if sessionToken is not None:
|
|
try:
|
|
session = getProxyForServerRole('sessions').getSession(
|
|
sessionToken, req.connection.remote_ip)
|
|
except faults.UnknownSessionToken:
|
|
if sessionTokenInUrl:
|
|
uri = cleanUpUnparsedUri(['sessionToken'], 'http')
|
|
return redirect(uri)
|
|
else:
|
|
sessionToken = None
|
|
sessionTokenInCookie = 0
|
|
except faults.InvalidSessionToken:
|
|
session = None
|
|
sessionToken = None
|
|
except faults.UnknownServerId:
|
|
# no SessionServer, no problem
|
|
session = None
|
|
sessionToken = None
|
|
if sessionToken is not None and not sessionTokenInCookie:
|
|
# The sessionToken is valid but is not stored in the cookie.
|
|
# So, don't try to use the cookie.
|
|
canUseCookie = 0
|
|
context.setVar('canUseCookie', canUseCookie)
|
|
context.setVar('session', session)
|
|
context.setVar('sessionToken', sessionToken)
|
|
context.setVar('sessionTokenInCookie', sessionTokenInCookie)
|
|
userToken = ''
|
|
userId = None
|
|
if session and session.has_key('userToken') and session['userToken']:
|
|
userToken = session['userToken']
|
|
context.setVar('userToken', userToken)
|
|
userId = getProxyForServerRole('authentication').getUserId()
|
|
if userId:
|
|
userToken = session['userToken']
|
|
else:
|
|
userToken = None
|
|
userId = None
|
|
del session['userToken']
|
|
if session.has_key('userId'):
|
|
del session['userId']
|
|
session['isDirty'] = 1
|
|
context.setVar('userId', userId)
|
|
context.setVar('userToken', userToken)
|
|
|
|
if not userToken and req.headers_in.has_key('Authorization'):
|
|
authorization = req.headers_in['Authorization']
|
|
tokens = authorization.split()
|
|
if len(tokens) != 2 or tokens[0] != 'Basic':
|
|
raise apache.SERVER_RETURN, apache.HTTP_NOT_ACCEPTABLE
|
|
# TODO: http auth
|
|
|
|
|
|
# Handle preferences
|
|
preferences = None
|
|
if userToken:
|
|
try:
|
|
preferences = getWebForServerRole(
|
|
'preferences').getPreference()
|
|
except faults.UnknownServerId:
|
|
pass
|
|
else:
|
|
context.setVar('preferences', preferences)
|
|
|
|
# Handle languages.
|
|
languages = []
|
|
if session and session.has_key('lang'):
|
|
languages = [session['lang']]
|
|
elif preferences is not None and preferences.language \
|
|
and preferences.language != 'None':
|
|
languages = [preferences.language]
|
|
elif req.headers_in.has_key('Accept-Language'):
|
|
try:
|
|
languages = req.headers_in['Accept-Language']
|
|
languages = languages.split(',')
|
|
languages = [x.strip()[:2] for x in languages]
|
|
except: # TODO: tighter check
|
|
return HTTP_BAD_REQUEST
|
|
translationsProxy = getProxyForServerRole('translations')
|
|
languages = [language == 'C' and 'en' or language
|
|
for language in languages]
|
|
try:
|
|
possibleLanguages = translationsProxy.getPossibleLanguages()
|
|
except (faults.UnknownServerId, faults.UnknownDispatcherInId):
|
|
possibleLanguages = []
|
|
if not virtualHost.language in possibleLanguages:
|
|
possibleLanguages.append(virtualHost.language)
|
|
languages = [x for x in languages if x in possibleLanguages]
|
|
if not languages:
|
|
languages = [virtualHost.language]
|
|
context.setVar('readLanguages', languages)
|
|
|
|
self.setLanguage()
|
|
|
|
|
|
# Handle web cache.
|
|
reqCache = None
|
|
if sessionToken is None:
|
|
reqCache = RequestCache(req, languages[0])
|
|
reqCache.cacheTime = context.getVar('cacheTime')
|
|
if not virtualHost.cacheFiles:
|
|
reqCache.caching = -1
|
|
modTime = reqCache.checkDepends()
|
|
reqCache = None
|
|
else:
|
|
req.headers_out['Expires'] = reqCache.getExpires()
|
|
modTime = None
|
|
if reqCache and reqCache.isPageCached():
|
|
try:
|
|
cachedPage, contentType = reqCache.getCachePage()
|
|
except OSError:
|
|
# file was removed just on the wrong time
|
|
cachedPage = None
|
|
|
|
if cachedPage is not None:
|
|
setHttpCookie()
|
|
if context.getVar('debug'):
|
|
req.headers_out['X-Glasnost-CachedFile'] = 'yep'
|
|
return self.outputStaticFile(
|
|
req, cachedPage, modTime, contentType)
|
|
elif context.getVar('useBufferedRequests'):
|
|
reqCache = BufferedRequest(req)
|
|
reqCache.caching = 0
|
|
reqCache.depends = []
|
|
|
|
if reqCache is None:
|
|
# The system caching is not activated when a session is open.
|
|
reqCache = Request(req)
|
|
reqCache.caching = 0
|
|
reqCache.depends = []
|
|
reqCache.buffered = 0
|
|
context.setVar('req', reqCache)
|
|
|
|
# Handle the httpLocalPath in URL and call appropriate function.
|
|
knownRoles = getRegisteredRoles(dispatcherId)
|
|
context.setVar('knownRoles', knownRoles)
|
|
context.setVar('templateFileName', 'template.html')
|
|
context.setVar('templatePrefix', '')
|
|
result = self.parseHttpPath([x for x in httpLocalPath.split('/') if x])
|
|
if result is not None:
|
|
return result
|
|
return HTTP_NOT_FOUND
|
|
|
|
def loadConfigOptions(self):
|
|
applications.Application.loadConfigOptions(self)
|
|
|
|
configContext = context.get(_level = 'config')
|
|
|
|
varDirectoryPath = commonTools.getConfig(
|
|
'Misc', 'vardir', default = '/var/lib/glasnost')
|
|
configContext.setVar('varDirectoryPath', varDirectoryPath)
|
|
|
|
defaultWebDirectoryPath = commonTools.getConfig(
|
|
'Misc', 'WebDirectoryPath')
|
|
configContext.setVar(
|
|
'defaultWebDirectoryPath', defaultWebDirectoryPath)
|
|
|
|
def loadWebConfigOptions(self):
|
|
handlerContext = context.get(_level = 'handler')
|
|
|
|
virtualHost = context.getVar('virtualHost')
|
|
|
|
dispatcherId = virtualHost.defaultDispatcherId
|
|
# Ensure that dispatcherId is valid.
|
|
dispatcherId = commonTools.extractDispatcherId(dispatcherId)
|
|
handlerContext.setVar('dispatcherId', dispatcherId)
|
|
handlerContext.setVar('defaultDispatcherId', dispatcherId)
|
|
|
|
handlerContext.setVar('applicationId', commonTools.makeApplicationId(
|
|
dispatcherId, self.applicationRole))
|
|
|
|
virtualHost.useHTTPS = \
|
|
webTools.getConfig('UseHTTPS', 'false') == 'true'
|
|
virtualHost.cacheFiles = \
|
|
webTools.getConfig('CacheFiles', 'false') == 'true'
|
|
|
|
debug = webTools.getConfig('Debug', 'false') == 'true'
|
|
handlerContext.setVar('debug', debug)
|
|
|
|
debugTal = webTools.getConfig('DebugTAL', 'false') == 'true'
|
|
handlerContext.setVar('debugTal', debugTal)
|
|
|
|
profiling = webTools.getConfig('Profiling', 'false') == 'true'
|
|
handlerContext.setVar('profiling', profiling)
|
|
|
|
sectionLevel = int(
|
|
webTools.getConfig('sectionLevel', default = 1))
|
|
handlerContext.setVar('sectionLevel', sectionLevel)
|
|
|
|
cacheTime = int(webTools.getConfig('CacheTime', '15')) * 60
|
|
handlerContext.setVar('cacheTime', cacheTime)
|
|
|
|
useBufferedRequests = webTools.getConfig(
|
|
'UseBufferedRequests', 'false') == 'true'
|
|
handlerContext.setVar('useBufferedRequests', useBufferedRequests)
|
|
|
|
talExtensionsDirectoryPaths = []
|
|
talExtensionsDirectoryPath = commonTools.getConfig(
|
|
context.getVar('dispatcherId'), 'TalExtensionsDirectoryPath')
|
|
if talExtensionsDirectoryPath:
|
|
talExtensionsDirectoryPaths.append(talExtensionsDirectoryPath)
|
|
talExtensionsDirectoryPaths.append(
|
|
os.path.join(context.getVar('varDirectoryPath'), 'extensions'))
|
|
handlerContext.setVar('talExtensionsDirectoryPaths',
|
|
talExtensionsDirectoryPaths)
|
|
|
|
templatesDirectoryPath = commonTools.getConfig(
|
|
context.getVar('dispatcherId'), 'TemplatesDirectoryPath',
|
|
default = commonTools.getConfig('Misc', 'TemplatesDirectoryPath'))
|
|
if templatesDirectoryPath and virtualHost.templateDirectoryName:
|
|
directoryName = '%s/%s/web' % (templatesDirectoryPath,
|
|
virtualHost.templateDirectoryName)
|
|
if os.path.exists(directoryName):
|
|
context.getVar('webDirectoryPaths').insert(0, directoryName)
|
|
|
|
webDirectoryPath = commonTools.getConfig(
|
|
virtualHost.hostName, 'WebDirectoryPath')
|
|
if webDirectoryPath and os.path.exists(webDirectoryPath):
|
|
context.getVar('webDirectoryPaths').insert(0, webDirectoryPath)
|
|
|
|
webDirectoryPath = context.getVar('defaultWebDirectoryPath')
|
|
if webDirectoryPath and os.path.exists(webDirectoryPath):
|
|
context.getVar('webDirectoryPaths').append(webDirectoryPath)
|
|
|
|
fallbackTemplatesDirectoryPath = commonTools.getConfig(
|
|
'Misc', 'TemplatesDirectoryPath', default=None)
|
|
if not templatesDirectoryPath:
|
|
# configuration file changed between 0.5.4 and 0.5.5. This
|
|
# fallbacks to the old configuration. Will be removed someday.
|
|
templatesDirectoryPath = commonTools.getConfig(
|
|
'Templates', 'Directory') + '/../'
|
|
fallbackTemplatesDirectoryPath = templatesDirectoryPath
|
|
handlerContext.setVar('templatesDirectoryPath', templatesDirectoryPath)
|
|
handlerContext.setVar('fallbackTemplatesDirectoryPath',
|
|
fallbackTemplatesDirectoryPath)
|
|
|
|
templateDirectoryName = commonTools.getConfig(
|
|
'Misc', 'DefaultTemplate')
|
|
if virtualHost.templateDirectoryName is not None:
|
|
templateDirectoryName = virtualHost.templateDirectoryName
|
|
handlerContext.setVar('templateDirectoryName', templateDirectoryName)
|
|
|
|
scriptsDirectoryPaths = []
|
|
scriptsDirectoryPath = commonTools.getConfig(
|
|
context.getVar('dispatcherId'), 'ScriptDirectoryPath')
|
|
if scriptsDirectoryPath:
|
|
scriptsDirectoryPaths.append(scriptsDirectoryPath)
|
|
if templatesDirectoryPath \
|
|
and not templatesDirectoryPath in scriptsDirectoryPaths:
|
|
scriptsDirectoryPaths.append(templatesDirectoryPath)
|
|
handlerContext.setVar('scriptsDirectoryPaths', scriptsDirectoryPaths)
|
|
|
|
enableSpellChecking = webTools.getConfig(
|
|
'DisableSpellchecking', 'false') != 'true'
|
|
handlerContext.setVar('enableSpellChecking', enableSpellChecking)
|
|
|
|
adminEmailAddress = commonTools.getConfig(
|
|
context.getVar('dispatcherId'), 'AdminEmailAddress',
|
|
default = commonTools.getConfig(
|
|
'Mail', 'Admin', default = 'root@localhost'))
|
|
handlerContext.setVar('adminEmailAddress', adminEmailAddress)
|
|
|
|
def outputStaticFile(self, req, data, lastModTime = None, mimeType = None):
|
|
if gzip and req.headers_in.has_key('Accept-Encoding'):
|
|
value = req.headers_in['Accept-Encoding']
|
|
if value.find('gzip') != -1 and value.find('gzip;q=0') == -1:
|
|
zbuf = cStringIO.StringIO()
|
|
zfile = gzip.GzipFile(mode = 'wb', fileobj = zbuf)
|
|
zfile.write(data)
|
|
zfile.close()
|
|
data = zbuf.getvalue()
|
|
req.headers_out['Content-Encoding'] = 'gzip'
|
|
t1 = context.getVar('t1')
|
|
if lastModTime:
|
|
req.headers_out['Last-Modified'] = time.strftime(
|
|
'%a, %d %b %Y %H:%M:%S GMT', lastModTime)
|
|
req.headers_out['Content-Length'] = '%d' % len(data)
|
|
# TODO: could also output Content-MD5
|
|
if lastModTime and req.headers_in.has_key('If-Modified-Since'):
|
|
# we don't want to use bandwith if the file was not modified
|
|
try:
|
|
t = time.strptime(req.headers_in['If-Modified-Since'][:25],
|
|
'%a, %d %b %Y %H:%M:%S')
|
|
if lastModTime <= t:
|
|
return HTTP_NOT_MODIFIED
|
|
except (ValueError, KeyError):
|
|
pass
|
|
if mimeType:
|
|
req.content_type = mimeType
|
|
req.send_http_header()
|
|
if req.method != 'HEAD':
|
|
try:
|
|
req.write(data)
|
|
except IOError:
|
|
# the user probably cancelled the request
|
|
return OK
|
|
return OK
|
|
|
|
def parseHttpPath(self, remaining):
|
|
context.push()
|
|
try:
|
|
result = self.parseHttpPathRemote(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathLanguage(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathWorkspace(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathAlias(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathServerRole(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathTalFile(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathModule(remaining)
|
|
if result is not None:
|
|
return result
|
|
return None
|
|
finally:
|
|
context.pull()
|
|
|
|
def parseHttpPathAction(self, remaining):
|
|
context.push()
|
|
try:
|
|
if not remaining:
|
|
action = None
|
|
else:
|
|
action = remaining[0]
|
|
remaining = remaining[1:]
|
|
if action is not None and '-' in action:
|
|
splittedAction = [x for x in action.split('-') if x]
|
|
if len(splittedAction) >= 2:
|
|
template = '-'.join(splittedAction[:-1])
|
|
context.setVar(
|
|
'templateFileName', 'template-%s.html' % template)
|
|
context.setVar('templatePrefix', '%s-' % template)
|
|
action = splittedAction[-1]
|
|
serverRole = context.getVar('serverRole')
|
|
web = getWebForServerRole(serverRole)
|
|
context.setVar('web', web)
|
|
function = getAppropriateFunction(
|
|
serverRole, context.getVar('objectId'), action)
|
|
if function is None:
|
|
if context.getVar('objectId') is not None:
|
|
if action is not None:
|
|
remaining.insert(0, action)
|
|
web = getWebForServerRole(context.getVar('serverRole'))
|
|
if hasattr(web, 'parseHttpPathAction'):
|
|
return web.parseHttpPathAction(remaining)
|
|
return None
|
|
context.setVar('function', function)
|
|
|
|
result = self.parseHttpPathArguments(remaining)
|
|
if result is not None:
|
|
return result
|
|
return None
|
|
finally:
|
|
context.pull()
|
|
|
|
def parseHttpPathAlias(self, remaining):
|
|
context.push()
|
|
try:
|
|
assert context.getVar('objectId') is None
|
|
assert context.getVar('serverRole') is None
|
|
if not remaining:
|
|
return None
|
|
if not 'pagenames' in context.getVar('knownRoles'):
|
|
return None
|
|
pageNamesProxy = getProxyForServerRole('pagenames')
|
|
try:
|
|
path = ''
|
|
num = 1
|
|
lastKnown, lastNum = '', 0
|
|
for r in remaining:
|
|
path += r
|
|
objectId = getWebForServerRole('pagenames').getIdByName(
|
|
path, serverId = context.getVar('dispatcherId'))
|
|
if objectId:
|
|
lastKnown, lastNum = objectId, num
|
|
if not pageNamesProxy.hasObjectStartingWithPath(path,
|
|
serverId = context.getVar('dispatcherId')):
|
|
break
|
|
path += '/'
|
|
num += 1
|
|
except faults.UnknownServerId:
|
|
return None
|
|
objectId, num = lastKnown, lastNum
|
|
if not objectId:
|
|
return None
|
|
context.setVar('dispatcherId',
|
|
commonTools.extractDispatcherId(objectId))
|
|
context.setVar('serverRole', commonTools.extractRole(objectId))
|
|
context.setVar('objectId', objectId)
|
|
remaining = remaining[num:]
|
|
|
|
result = self.parseHttpPathAction(remaining)
|
|
if result is not None:
|
|
return result
|
|
return None
|
|
finally:
|
|
context.pull()
|
|
|
|
def parseHttpPathArguments(self, remaining):
|
|
context.push()
|
|
try:
|
|
if remaining:
|
|
context.setVar('positionalArguments', remaining)
|
|
remaining = []
|
|
return self.callFunction()
|
|
finally:
|
|
context.pull()
|
|
|
|
def parseHttpPathId(self, remaining):
|
|
context.push()
|
|
try:
|
|
assert context.getVar('objectId') is None
|
|
assert context.getVar('serverRole') is not None
|
|
web = getWebForServerRole(context.getVar('serverRole'))
|
|
if hasattr(web, 'parseHttpPathId'):
|
|
return web.parseHttpPathId(remaining)
|
|
if not remaining:
|
|
return None
|
|
objectId = '%s/%s/%s' % (
|
|
context.getVar('dispatcherId'),
|
|
context.getVar('serverRole'),
|
|
remaining[0])
|
|
remaining = remaining[1:]
|
|
context.setVar('objectId', objectId)
|
|
result = self.parseHttpPathAction(remaining)
|
|
if result is not None:
|
|
return result
|
|
return None
|
|
finally:
|
|
context.pull()
|
|
|
|
def parseHttpPathLanguage(self, remaining):
|
|
context.push()
|
|
try:
|
|
translationsProxy = getProxyForServerRole('translations')
|
|
if not translationsProxy:
|
|
return
|
|
if len(remaining) < 1:
|
|
return None
|
|
try:
|
|
if remaining[0] not in translationsProxy.getPossibleLanguages(
|
|
serverId = context.getVar('dispatcherId')):
|
|
return None
|
|
except faults.UnknownDispatcherInId:
|
|
return None
|
|
languages = [remaining[0]]
|
|
context.setVar('readLanguages', languages)
|
|
context.setVar('languageSetInUrl', 1)
|
|
self.setLanguage()
|
|
remaining = remaining[1:]
|
|
|
|
result = self.parseHttpPathWorkspace(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathAlias(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathServerRole(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathTalFile(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathModule(remaining)
|
|
if result is not None:
|
|
return result
|
|
return None
|
|
finally:
|
|
context.pull()
|
|
|
|
def parseHttpPathModule(self, remaining):
|
|
context.push()
|
|
try:
|
|
if remaining:
|
|
moduleName = remaining[0]
|
|
if moduleName.endswith('.py'):
|
|
moduleName = moduleName[:-len('.py')]
|
|
if len(remaining) >= 2:
|
|
functionName = remaining[1]
|
|
if functionName[0] == '_':
|
|
return None
|
|
remaining = remaining[2:]
|
|
else:
|
|
functionName = 'index'
|
|
remaining = remaining[1:]
|
|
else:
|
|
moduleName = 'index'
|
|
functionName = 'index'
|
|
# FIXME HTTP errors should be handled better than that:
|
|
if moduleName == HTTP_NOT_FOUND:
|
|
return moduleName
|
|
try:
|
|
if sys.modules.has_key(moduleName):
|
|
module = sys.modules[moduleName]
|
|
else:
|
|
module = apache.import_module(
|
|
moduleName,
|
|
context.getVar('req')._req,
|
|
context.getVar('webDirectoryPaths'))
|
|
except ImportError:
|
|
if context.getVar('debug'):
|
|
raise
|
|
return None
|
|
if not hasattr(module, functionName):
|
|
remaining.insert(0, functionName)
|
|
functionName = 'index'
|
|
try:
|
|
function = getattr(module, functionName)
|
|
except AttributeError:
|
|
if context.getVar('debug'):
|
|
raise
|
|
return None
|
|
if type(function) != types.FunctionType:
|
|
return None
|
|
# Build a fake serverRole (needed by X.xxxUrl functions).
|
|
context.setVar('serverRole', moduleName)
|
|
context.setVar('function', function)
|
|
|
|
result = self.parseHttpPathArguments(remaining)
|
|
if result is not None:
|
|
return result
|
|
return None
|
|
finally:
|
|
context.pull()
|
|
|
|
def parseHttpPathRemote(self, remaining):
|
|
context.push()
|
|
try:
|
|
assert context.getVar('serverRole') is None
|
|
if len(remaining) < 2:
|
|
return None
|
|
if remaining[0] != 'remote':
|
|
return None
|
|
dispatcherHostName = remaining[1]
|
|
context.setVar('dispatcherId',
|
|
'glasnost://%s' % dispatcherHostName)
|
|
remaining = remaining[2:]
|
|
|
|
result = self.parseHttpPathLanguage(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathWorkspace(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathAlias(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathServerRole(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathTalFile(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathModule(remaining)
|
|
if result is not None:
|
|
return result
|
|
return None
|
|
finally:
|
|
context.pull()
|
|
|
|
def parseHttpPathServerRole(self, remaining):
|
|
context.push()
|
|
try:
|
|
assert context.getVar('serverRole') is None
|
|
if not remaining:
|
|
return None
|
|
if not remaining[0] in context.getVar('knownRoles'):
|
|
return None
|
|
context.setVar('serverRole', remaining[0])
|
|
remaining = remaining[1:]
|
|
|
|
result = self.parseHttpPathAction(remaining)
|
|
if result is not None:
|
|
return result
|
|
result = self.parseHttpPathId(remaining)
|
|
if result is not None:
|
|
return result
|
|
return None
|
|
finally:
|
|
context.pull()
|
|
|
|
def parseHttpPathTalFile(self, remaining):
|
|
context.push()
|
|
try:
|
|
if remaining:
|
|
fileNameCore = remaining[0]
|
|
if fileNameCore.endswith('.tal'):
|
|
fileNameCore = fileNameCore[:-len('.tal')]
|
|
elif fileNameCore.endswith('.xtal'):
|
|
fileNameCore = fileNameCore[:-len('.xtal')]
|
|
remaining = remaining[1:]
|
|
else:
|
|
fileNameCore = 'index'
|
|
for webDirectoryPath in context.getVar('webDirectoryPaths'):
|
|
talFilePathCore = os.path.join(webDirectoryPath, fileNameCore)
|
|
talFilePath = '%s.tal' % talFilePathCore
|
|
xtalFilePath = '%s.xtal' % talFilePathCore
|
|
if os.path.exists(talFilePath):
|
|
function = processTALFile
|
|
remaining.insert(0, talFilePath)
|
|
break
|
|
elif os.path.exists(xtalFilePath):
|
|
function = processXTALFile
|
|
remaining.insert(0, xtalFilePath)
|
|
break
|
|
else:
|
|
return None
|
|
# Build a fake serverRole (needed by X.xxxUrl functions).
|
|
context.setVar('serverRole', fileNameCore)
|
|
context.setVar('function', function)
|
|
|
|
result = self.parseHttpPathArguments(remaining)
|
|
if result is not None:
|
|
return result
|
|
return None
|
|
finally:
|
|
context.pull()
|
|
|
|
def parseHttpPathWorkspace(self, remaining):
|
|
context.push()
|
|
try:
|
|
assert context.getVar('objectId') is None
|
|
assert context.getVar('serverRole') is None
|
|
if not remaining:
|
|
return None
|
|
if not 'workspace' in context.getVar('knownRoles'):
|
|
return None
|
|
workspaceName = remaining[0]
|
|
if not 0: # test of workgroup existance
|
|
return None
|
|
# alter dispatcher ?
|
|
remaining = remaining[1:]
|
|
return self.parseHttpPath(remaining)
|
|
finally:
|
|
context.pull()
|
|
|
|
def setLanguage(self):
|
|
virtualHost = context.getVar('virtualHost')
|
|
languages = context.getVar('readLanguages')
|
|
try:
|
|
locale.setlocale(locale.LC_COLLATE, (languages[0], None))
|
|
except locale.Error:
|
|
locale.setlocale(locale.LC_COLLATE, 'C')
|
|
|
|
domains = [ '%s-web' % glasnost.applicationName ] + (
|
|
virtualHost.locales or [])
|
|
translation = commonTools.translation(domains, languages)
|
|
__builtin__.__dict__['_'] = translation.gettext
|
|
|
|
|
|
class BufferedRequest:
|
|
buffered = 1
|
|
|
|
def __init__(self, req):
|
|
self._req = req
|
|
self.buffer = cStringIO.StringIO()
|
|
|
|
def write(self, str):
|
|
self.buffer.write(str)
|
|
|
|
def finish(self):
|
|
self.buffer.seek(0)
|
|
self._req.write(self.buffer.read())
|
|
|
|
def __getattr__(self, attr):
|
|
try:
|
|
return getattr(self._req, attr)
|
|
except AttributeError:
|
|
raise AttributeError, attr
|
|
|
|
def __setattr__(self, attr, val):
|
|
try:
|
|
if attr != '_req':
|
|
setattr(self._req, attr, val)
|
|
else:
|
|
self.__dict__['_req'] = val
|
|
except AttributeError:
|
|
self.__dict__[attr] = val
|
|
|
|
class Request(apache.Request):
|
|
def write(self, st):
|
|
if type(st) is type(u''):
|
|
st = st.encode('iso-8859-1')
|
|
return self._req.write(st)
|
|
|
|
class RequestCache:
|
|
buffered = 0
|
|
cacheDirectoryPath = None
|
|
cacheFile = None
|
|
cacheFilePath = None
|
|
cacheFilePathDepends = None
|
|
cacheFilePathMimeType = None
|
|
cacheTime = 15 * 60 # Default cache time: 15 minutes.
|
|
caching = 1
|
|
depends = None
|
|
|
|
def __init__(self, req, language):
|
|
for key, value in req.__dict__.items():
|
|
self.__dict__[key] = value
|
|
self.cacheDirectoryPath = os.path.join(
|
|
context.getVar('varDirectoryPath'), 'webcache')
|
|
self.cacheFilePath = os.path.join(
|
|
self.cacheDirectoryPath,
|
|
self.getCacheFileName(language))
|
|
self.cacheFilePathDepends = os.path.join(
|
|
self.cacheDirectoryPath,
|
|
self.getCacheFileName(language) + '.depends')
|
|
self.cacheFilePathMimeType = os.path.join(
|
|
self.cacheDirectoryPath,
|
|
self.getCacheFileName(language) + '.mimetype')
|
|
self.depends = []
|
|
|
|
def cancel(self):
|
|
if self.cacheFile is not None:
|
|
self.cacheFile.close()
|
|
del self.cacheFile
|
|
if os.path.exists(self.cacheFilePath):
|
|
try:
|
|
os.unlink(self.cacheFilePath)
|
|
except OSError:
|
|
pass
|
|
|
|
def checkDepends(self):
|
|
if not os.path.exists(self.cacheFilePathDepends):
|
|
return
|
|
if not os.path.exists(self.cacheFilePath):
|
|
return
|
|
ids = open(self.cacheFilePathDepends).readlines()
|
|
modificationTime = 0
|
|
for id in ids:
|
|
web = getWeb(id)
|
|
object = web.getPartialObject(id, ['modificationTime'])
|
|
if not hasattr(object, 'modificationTime') or \
|
|
object.modificationTime > os.stat(self.cacheFilePath)[ST_CTIME]:
|
|
try:
|
|
os.unlink(self.cacheFilePath)
|
|
except OSError:
|
|
pass
|
|
break
|
|
if object.modificationTime > modificationTime:
|
|
modificationTime = object.modificationTime
|
|
else:
|
|
self.caching = 1
|
|
return time.gmtime(modificationTime)
|
|
|
|
def isPageCached(self):
|
|
return os.path.exists(self.cacheFilePath) \
|
|
and time.time() - \
|
|
os.stat(self.cacheFilePath)[ST_CTIME] <= self.cacheTime
|
|
|
|
def closeCachePage(self):
|
|
self.cacheFile.close()
|
|
del self.cacheFile
|
|
|
|
if self.caching == -1:
|
|
if self.depends:
|
|
try:
|
|
open(self.cacheFilePathDepends, 'w').write('\n'.join(
|
|
self.depends))
|
|
open(self.cacheFilePathMimeType, 'w').write(
|
|
self.content_type)
|
|
except IOError:
|
|
pass
|
|
else:
|
|
try:
|
|
os.unlink(self.cacheFilePath)
|
|
except OSError:
|
|
pass
|
|
elif self.caching == 1:
|
|
open(self.cacheFilePathMimeType, 'w').write(self.content_type)
|
|
|
|
|
|
def getCachePage(self):
|
|
self.cacheFile = open(self.cacheFilePath, 'r')
|
|
fcntl.lockf(self.cacheFile, fcntl.LOCK_SH)
|
|
cachePage = self.cacheFile.read()
|
|
fcntl.lockf(self.cacheFile, fcntl.LOCK_UN)
|
|
self.cacheFile.close()
|
|
if os.path.exists(self.cacheFilePathMimeType):
|
|
mimeType = open(self.cacheFilePathMimeType).read()
|
|
else:
|
|
mimeType = 'text/html'
|
|
return cachePage, mimeType
|
|
|
|
def getCacheFileName(self, language):
|
|
if self.headers_in.has_key('Host'):
|
|
hostName = self.headers_in['Host']
|
|
else:
|
|
hostName = ''
|
|
uri = self.unparsed_uri
|
|
return binascii.hexlify(md5.new(hostName + uri + language).digest())
|
|
|
|
def getExpires(self):
|
|
try:
|
|
t = os.stat(self.cacheFilePath)[ST_CTIME] + self.cacheTime
|
|
except OSError:
|
|
t = time.time() + self.cacheTime
|
|
return time.strftime('%a, %d %b %Y %H:%M:%S %Z', time.gmtime(t))
|
|
|
|
def openCachePage(self):
|
|
try:
|
|
self.cacheFile = open(self.cacheFilePath, 'w')
|
|
except IOError:
|
|
self.cacheFile = open('/dev/null', 'w')
|
|
|
|
def write(self, str):
|
|
self._req.write(str)
|
|
if self.cacheFile is not None:
|
|
fcntl.lockf(self.cacheFile, fcntl.LOCK_EX)
|
|
self.cacheFile.write(str)
|
|
fcntl.lockf(self.cacheFile, fcntl.LOCK_UN)
|
|
|
|
def __getattr__(self, attr):
|
|
try:
|
|
return getattr(self._req, attr)
|
|
except AttributeError:
|
|
raise AttributeError, attr
|
|
|
|
def __setattr__(self, attr, val):
|
|
try:
|
|
if attr != '_req':
|
|
setattr(self._req, attr, val)
|
|
else:
|
|
self.__dict__['_req'] = val
|
|
except AttributeError:
|
|
self.__dict__[attr] = val
|
|
|
|
|
|
def handler(req):
|
|
if req.method in ('OPTIONS', 'TRACE', 'CONNECT', 'PROPFIND'):
|
|
return HTTP_NOT_IMPLEMENTED
|
|
|
|
application = Application()
|
|
application.launch()
|
|
try:
|
|
result = application.handler(req)
|
|
except apache.SERVER_RETURN, e:
|
|
# those are legitimate and should be raised to modpython handler
|
|
raise
|
|
except Exception, e:
|
|
import traceback
|
|
f = cStringIO.StringIO()
|
|
traceback.print_exc(file = f)
|
|
f = f.getvalue()
|
|
webTools.sendTalkBack(req, f)
|
|
raise
|
|
return result
|
|
|
|
|
|
if webTools.getConfig('Profiling', 'false') == 'true':
|
|
_handler = handler
|
|
def handler(req):
|
|
import profile
|
|
prof = profile.Profile()
|
|
try:
|
|
prof = prof.runctx('global result; result = _handler(req)',
|
|
globals(), locals())
|
|
except SystemExit:
|
|
pass
|
|
prof.dump_stats('/tmp/glasnost-web-%s.prof' % \
|
|
os.path.split(req.filename)[1])
|
|
return result
|
|
|