# Copyright (C) 2004 Scott W. Dunlop <sdunlop at users.sourceforge.net>
#
# 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.
import os, sys
from traceback import format_exception,print_exception
from database import Database,DatabaseError
from server import Server,Response
from authenticator import NoneAuthenticator,authentication_schemes
from interface import collate_interfaces,create_interface,load_gateway,load_method
from exceptions import KeyError,ValueError,OSError
from errors import *
from types import StringType
class Wiki( object ):
__slots__ = (
'server', 'database', 'logFile', 'siteName', 'stylesheetPath',
'flagCacheStylesheets', 'authenticator', 'baseUrl', 'httpPort',
'method_interfaces', 'gateway_interfaces', 'lexicon', 'stylesheet'
)
def __init__( self, path, log=None, port=None, init = False ):
self.server = None
self.siteName = None
self.stylesheetPath = None
self.flagCacheStylesheets = None
self.authenticator = None
self.baseUrl = None
self.httpPort = port
self.stylesheet = None
self.logFile = None
self.database = Database( wiki = self )
if not self.database.connect( path, init ):
raise DatabaseError( "Could not connect to database at '%s'" % (path,) )
if log is None:
log = self.database.getConfig( 'log-path' )
if log is None:
log = path + ".log"
if isinstance( log, StringType ):
try:
self.logFile = open( log, "a" )
except IOError, exc:
self.logFile = sys.stderr
self.logLine(
"Could not access %s for logging. Using stderr.",
log
)
else:
self.logFile = log
self.lexicon = None
self.gateway_interfaces = None
self.method_interfaces = None
def parseGid( self, nextGroup ):
import grp
try:
return int( nextGroup )
except ValueError, err:
pass
try:
groupEntry = grp.getgrnam( nextGroup )
return groupEntry[2]
except KeyError, err:
self.logLine( "Could not determine the group ID for group '%s'.",
nextGroup
)
return None
def parseUid( self, nextUser ):
import pwd
try:
return int( nextUser )
except ValueError, err:
pass
try:
userEntry = pwd.getpwnam( nextUser )
return userEntry[2]
except KeyError, err:
self.logLine( "Could not determine the user ID for user '%s'.",
nextGroup
)
return None
def genInterfaces( self ):
if self.lexicon is None:
self.lexicon, self.gateway_interfaces, self.method_interfaces = (
collate_interfaces( self )
)
def runForever( self ):
self.genInterfaces()
server = self.getServer()
nextGroup = self.getDatabase().getConfig( "set-gid", None )
if nextGroup is not None:
gid = self.parseGid( nextGroup )
if gid is None: return
try:
os.setgid( gid )
except OSError, exc:
self.logLine(
"Could not change process group to %s -- try starting cloud-wiki as root.",
nextGroup
)
return
nextUser = self.getDatabase().getConfig( "set-uid", None )
if nextUser is not None:
uid = self.parseUid( nextUser )
if uid is None: return
try:
os.setuid( uid )
except OSError, exc:
self.logLine(
"Could not change process user to %s -- try starting cloud-wiki as root.",
nextUser
)
return
server.runForever()
def getServer( self ):
if self.server is None: self.genServer()
return self.server
def usingCodex( self ):
return self.server is not None
def getDatabase( self ):
return self.database
def genServer( self ):
self.server = Server( wiki = self, port = self.getHttpPort() )
def getStylesheetPath( self ):
if self.stylesheetPath is None: self.genStylesheetPath()
return self.stylesheetPath
def genStylesheetPath( self ):
db = self.getDatabase()
stylesheetPath = db.getConfig( "stylesheet-path", 'style.css' )
if os.path.isabs( stylesheetPath ):
self.stylesheetPath = stylesheetPath
else:
self.stylesheetPath = os.path.join(
db.getDirectory(), stylesheetPath
)
def shouldCacheStylesheets( self ):
if self.flagCacheStylesheets is None: self.checkCacheStylesheets()
return self.flagCacheStylesheets
def checkCacheStylesheets( self ):
self.flagCacheStylesheets = self.getDatabase().getConfig(
'cache-stylesheets', 'no'
).lower() in [
'true', 't', 'yes', 'y'
]
def getStylesheet( self ):
if self.stylesheet is None:
if self.shouldCacheStylesheets():
self.stylesheet = "\n".join(
open( self.getStylesheetPath(), "r" ).readlines()
)
else:
return "\n".join(
open( self.getStylesheetPath(), "r" ).readlines()
)
return self.stylesheet
def getLogFile( self ):
return self.logFile
def logLine( self, message, *args ):
self.logData( message + "\n", *args )
def logData( self, message, *args ):
lf = self.getLogFile()
if lf is None: return
if args:
lf.write( message % args )
else:
lf.write( message )
def getHttpPort( self ):
if self.httpPort is None: self.genHttpPort()
return self.httpPort
def genHttpPort( self ):
port = self.getDatabase().getConfig( 'http-port', None )
if port is not None:
self.httpPort = int( port )
else:
self.httpPort = 8080
def getSitename( self ):
if self.siteName is None: self.genSitename()
return self.siteName
def genSitename( self ):
self.siteName = self.getDatabase().getConfig(
"site-title", "AnonyWiki"
)
def getBaseUrl( self ):
if self.baseUrl is None:
self.genBaseUrl()
return self.baseUrl
def genBaseUrl( self ):
self.baseUrl = self.getDatabase().getConfig( "base-url", "" )
def shouldMentionUsers( self ):
return self.getAuthenticator().shouldMentionUsers()
def getAuthenticator( self ):
if self.authenticator is None:
self.genAuthenticator( )
return self.authenticator
def genAuthenticator( self ):
key = self.getDatabase().getConfig( 'site-auth' )
scheme = authentication_schemes.get( key )
if scheme is None:
scheme = NoneAuthenticator
self.authenticator = scheme( self )
def getGatewayFor( self, key ):
if self.gateway_interfaces is not None:
return self.gateway_interfaces.get( key )
else:
self.lexicon, interface = load_gateway( self, key, self.lexicon )
return interface
def getMethodFor( self, key ):
if self.method_interfaces is not None:
return self.method_interfaces.get( key )
else:
self.lexicon, interface = load_method( self, key, self.lexicon )
return interface
def getLexicon( self ):
return self.lexicon
def getVersion( self ):
return 2,1
def authenticate( self, req, res ):
"authenticates the user specified by the request, and may update the response with a session token;"
" will assign the request's user attribute if authentication was successful."
auth = self.getAuthenticator()
if req.getAction() in ['logout', 'Logout']:
res.addCookie( 'tk', None )
req.setAction( '' )
return
tk = req.getCookie( 'tk', None )
if tk is not None:
un = auth.getUserForToken( tk )
if un:
req.setUser( un )
return
args = req.getArgs()
un = args.get('un')
if un is None: return
pw = args.get('pw')
if pw is None: return
results = auth.authenticate( un, pw )
if results:
un, tk = results
res.addCookie( 'tk', tk )
req.setUser( un )
def respondTo( self, req ):
"invoked by a request when it has fully parsed the HTTP request"
res = Response()
try:
self.authenticate( req, res )
fg = self.getGatewayFor( req.getQuery() )
if fg:
self.getLexicon().execFragment(
fg, res.write, { 'req': req, 'res': res }
)
return res
fg = self.getMethodFor( req.getAction() )
if fg:
req.normalizeQuery()
self.getLexicon().execFragment(
fg, res.write, { 'req': req, 'res': res }
)
return res
raise InvalidRequestError( request=req.getAction() )
except IHateFavicons, exc:
res.setContent( exc.asHtml() )
res.setCode( exc.httpCode )
return res
except Exception, exc:
for line in format_exception( *sys.exc_info() ):
self.logLine( line )
try:
res.setContent( exc.asHtml() )
res.setCode( exc.httpCode )
except:
res.setCode( 500 )
res.setContent( "<HTML><BODY>Error 500 -- Internal Server Error</BODY></HTML>" )
return res
|