X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=roundup-server;h=5c8222c601d6e1137ef7701c9bf3395251dc7f09;hb=efd4113bd62606c449734539625e930c738f0856;hp=9b96638a97f122714b3a0d240ea24bfccb5d6fbc;hpb=bd4bb53298087373923b93996a632d4586d90bda;p=roundup.git diff --git a/roundup-server b/roundup-server index 9b96638..5c8222c 100755 --- a/roundup-server +++ b/roundup-server @@ -1,28 +1,36 @@ #!/usr/bin/python +# +# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/) +# This module is free software, and you may redistribute it and/or modify +# under the same terms as Python, so long as this copyright message and +# disclaimer are retained in their original form. +# +# IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR +# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING +# OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" +# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, +# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +# """ HTTP Server that serves roundup. -Stolen from CGIHTTPServer - -$Id: roundup-server,v 1.9 2001-08-05 07:44:36 richard Exp $ - +$Id: roundup-server,v 1.25 2002-01-05 02:21:21 richard Exp $ """ -import sys -if int(sys.version[0]) < 2: - print "Content-Type: text/plain\n" - print "Roundup requires Python 2.0 or newer." - sys.exit(0) -__version__ = "0.1" +# python version check +from roundup import version_check -__all__ = ["RoundupRequestHandler"] - -import os, urllib, StringIO, traceback, cgi, binascii, string, getopt, imp +import sys, os, urllib, StringIO, traceback, cgi, binascii, getopt, imp import BaseHTTPServer -import SimpleHTTPServer # Roundup modules of use here from roundup import cgitb, cgi_client import roundup.instance +from roundup.i18n import _ # ## Configuration @@ -33,6 +41,9 @@ ROUNDUP_INSTANCE_HOMES = { 'bar': '/tmp/bar', } +ROUNDUP_USER = None + + # Where to log debugging information to. Use an instance of DevNull if you # don't want to log anywhere. # TODO: actually use this stuff @@ -47,12 +58,9 @@ ROUNDUP_INSTANCE_HOMES = { # -class RoundupRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): +class RoundupRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): ROUNDUP_INSTANCE_HOMES = ROUNDUP_INSTANCE_HOMES - def send_head(self): - """Version of send_head that support CGI scripts""" - # TODO: actually do the HEAD ... - return self.run_cgi() + ROUNDUP_USER = ROUNDUP_USER def run_cgi(self): """ Execute the CGI command. Wrap an innner call in an error @@ -62,18 +70,21 @@ class RoundupRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): sys.stdin = self.rfile try: self.inner_run_cgi() + except cgi_client.NotFound: + self.send_error(404, self.path) except cgi_client.Unauthorised: - self.wfile.write('Content-Type: text/html\n') - self.wfile.write('Status: 403\n') - self.wfile.write('Unauthorised') + self.send_error(403, self.path) except: + # it'd be nice to be able to detect if these are going to have + # any effect... + self.send_response(400) + self.send_header('Content-Type', 'text/html') + self.end_headers() try: reload(cgitb) - self.wfile.write("Content-Type: text/html\n\n") self.wfile.write(cgitb.breaker()) self.wfile.write(cgitb.html()) except: - self.wfile.write("Content-Type: text/html\n\n") self.wfile.write("
")
                 s = StringIO.StringIO()
                 traceback.print_exc(None, s)
@@ -81,6 +92,23 @@ class RoundupRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
                 self.wfile.write("
\n") sys.stdin = save_stdin + do_GET = do_POST = do_HEAD = send_head = run_cgi + + def index(self): + ''' Print up an index of the available instances + ''' + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + w = self.wfile.write + w(_('Roundup instances index\n')) + w(_('

Roundup instances index

    \n')) + for instance in self.ROUNDUP_INSTANCE_HOMES.keys(): + w(_('
  1. %(instance_name)s\n')%{ + 'instance_url': urllib.quote(instance), + 'instance_name': cgi.escape(instance)}) + w(_('
')) + def inner_run_cgi(self): ''' This is the inner part of the CGI handling ''' @@ -94,14 +122,14 @@ class RoundupRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): # figure the instance if rest == '/': - raise ValueError, 'No instance specified' - l_path = string.split(rest, '/') - instance = urllib.unquote(l_path[1]) - if self.ROUNDUP_INSTANCE_HOMES.has_key(instance): - instance_home = self.ROUNDUP_INSTANCE_HOMES[instance] + return self.index() + l_path = rest.split('/') + instance_name = urllib.unquote(l_path[1]) + if self.ROUNDUP_INSTANCE_HOMES.has_key(instance_name): + instance_home = self.ROUNDUP_INSTANCE_HOMES[instance_name] instance = roundup.instance.open(instance_home) else: - raise ValueError, 'No such instance "%s"'%instance + raise cgi_client.NotFound # figure out what the rest of the path is if len(l_path) > 2: @@ -111,6 +139,7 @@ class RoundupRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): # Set up the CGI environment env = {} + env['INSTANCE_NAME'] = instance_name env['REQUEST_METHOD'] = self.command env['PATH_INFO'] = urllib.unquote(rest) if query: @@ -132,82 +161,14 @@ class RoundupRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): decoded_query = query.replace('+', ' ') - # if root, setuid to nobody - # TODO why isn't this done much earlier? - say, in main()? - if not os.getuid(): - nobody = nobody_uid() - os.setuid(nobody) - - # reload all modules - # TODO check for file timestamp changes and dependencies - #reload(date) - #reload(hyperdb) - #reload(roundupdb) - #reload(htmltemplate) - #reload(cgi_client) - #sys.path.insert(0, module_path) - #try: - # reload(instance) - #finally: - # del sys.path[0] - - # initialise the roundupdb, check for auth - db = instance.open('admin') - message = 'Unauthorised' - auth = self.headers.getheader('authorization') - if auth: - l = binascii.a2b_base64(auth.split(' ')[1]).split(':') - user = l[0] - password = None - if len(l) > 1: - password = l[1] - try: - uid = db.user.lookup(user) - except KeyError: - auth = None - message = 'Username not recognised' - else: - if password != db.user.get(uid, 'password'): - message = 'Incorrect password' - auth = None - db.close() - del db - if not auth: - self.send_response(401) - self.send_header('Content-Type', 'text/html') - self.send_header('WWW-Authenticate', 'basic realm="Roundup"') - self.end_headers() - self.wfile.write(message) - return - - self.send_response(200, "Script output follows") - # do the roundup thang - db = instance.open(user) - client = instance.Client(self.wfile, db, env, user) + client = instance.Client(instance, self, env) client.main() - do_POST = run_cgi - -nobody = None - -def nobody_uid(): - """Internal routine to get nobody's uid""" - global nobody - if nobody: - return nobody - try: - import pwd - except ImportError: - return -1 - try: - nobody = pwd.getpwnam('nobody')[2] - except KeyError: - nobody = 1 + max(map(lambda x: x[2], pwd.getpwall())) - return nobody def usage(message=''): - if message: message = 'Error: %s\n'%message - print '''%sUsage: + if message: + message = _('Error: %(error)s\n\n')%{'error': message} + print _('''%(message)sUsage: roundup-server [-n hostname] [-p port] [name=instance home]* -n: sets the host name @@ -220,7 +181,7 @@ roundup-server [-n hostname] [-p port] [name=instance home]* "roundup-admin init". You may specify any number of these name=home pairs on the command-line. For convenience, you may edit the ROUNDUP_INSTANCE_HOMES variable in the roundup-server file instead. -'''%message +''')%locals() sys.exit(0) def main(): @@ -228,28 +189,58 @@ def main(): port = 8080 try: # handle the command-line args - optlist, args = getopt.getopt(sys.argv[1:], 'n:p:') + try: + optlist, args = getopt.getopt(sys.argv[1:], 'n:p:u:') + except getopt.GetoptError, e: + usage(str(e)) + + user = ROUNDUP_USER for (opt, arg) in optlist: if opt == '-n': hostname = arg elif opt == '-p': port = int(arg) + elif opt == '-u': user = arg elif opt == '-h': usage() + if hasattr(os, 'getuid'): + # if root, setuid to the running user + if not os.getuid() and user is not None: + try: + import pwd + except ImportError: + raise ValueError, _("Can't change users - no pwd module") + try: + uid = pwd.getpwnam(user)[2] + except KeyError: + raise ValueError, _("User %(user)s doesn't exist")%locals() + os.setuid(uid) + elif os.getuid() and user is not None: + print _('WARNING: ignoring "-u" argument, not root') + + # People can remove this check if they're really determined + if not os.getuid() and user is None: + raise ValueError, _("Can't run as root!") + # handle instance specs if args: d = {} for arg in args: - name, home = string.split(arg, '=') + try: + name, home = arg.split('=') + except ValueError: + raise ValueError, _("Instances must be name=home") d[name] = home RoundupRequestHandler.ROUNDUP_INSTANCE_HOMES = d + except SystemExit: + raise except: - type, value = sys.exc_info()[:2] - usage('%s: %s'%(type, value)) + exc_type, exc_value = sys.exc_info()[:2] + usage('%s: %s'%(exc_type, exc_value)) # we don't want the cgi module interpreting the command-line args ;) sys.argv = sys.argv[:1] address = (hostname, port) httpd = BaseHTTPServer.HTTPServer(address, RoundupRequestHandler) - print 'Roundup server started on', address + print _('Roundup server started on %(address)s')%locals() httpd.serve_forever() if __name__ == '__main__': @@ -257,6 +248,98 @@ if __name__ == '__main__': # # $Log: not supported by cvs2svn $ +# Revision 1.24 2002/01/05 02:19:03 richard +# i18n'ification +# +# Revision 1.23 2001/12/15 23:47:07 richard +# sys module went away... +# +# Revision 1.22 2001/12/13 00:20:01 richard +# . Centralised the python version check code, bumped version to 2.1.1 (really +# needs to be 2.1.2, but that isn't released yet :) +# +# Revision 1.21 2001/12/02 05:06:16 richard +# . We now use weakrefs in the Classes to keep the database reference, so +# the close() method on the database is no longer needed. +# I bumped the minimum python requirement up to 2.1 accordingly. +# . #487480 ] roundup-server +# . #487476 ] INSTALL.txt +# +# I also cleaned up the change message / post-edit stuff in the cgi client. +# There's now a clearly marked "TODO: append the change note" where I believe +# the change note should be added there. The "changes" list will obviously +# have to be modified to be a dict of the changes, or somesuch. +# +# More testing needed. +# +# Revision 1.20 2001/11/26 22:55:56 richard +# Feature: +# . Added INSTANCE_NAME to configuration - used in web and email to identify +# the instance. +# . Added EMAIL_SIGNATURE_POSITION to indicate where to place the roundup +# signature info in e-mails. +# . Some more flexibility in the mail gateway and more error handling. +# . Login now takes you to the page you back to the were denied access to. +# +# Fixed: +# . Lots of bugs, thanks Roché and others on the devel mailing list! +# +# Revision 1.19 2001/11/12 22:51:04 jhermann +# Fixed option & associated error handling +# +# Revision 1.18 2001/11/01 22:04:37 richard +# Started work on supporting a pop3-fetching server +# Fixed bugs: +# . bug #477104 ] HTML tag error in roundup-server +# . bug #477107 ] HTTP header problem +# +# Revision 1.17 2001/10/29 23:55:44 richard +# Fix to CGI top-level index (thanks Juergen Hermann) +# +# Revision 1.16 2001/10/27 00:12:21 richard +# Fixed roundup-server for windows, thanks Juergen Hermann. +# +# Revision 1.15 2001/10/12 02:23:26 richard +# Didn't clean up after myself :) +# +# Revision 1.14 2001/10/12 02:20:32 richard +# server now handles setuid'ing much better +# +# Revision 1.13 2001/10/05 02:23:24 richard +# . roundup-admin create now prompts for property info if none is supplied +# on the command-line. +# . hyperdb Class getprops() method may now return only the mutable +# properties. +# . Login now uses cookies, which makes it a whole lot more flexible. We can +# now support anonymous user access (read-only, unless there's an +# "anonymous" user, in which case write access is permitted). Login +# handling has been moved into cgi_client.Client.main() +# . The "extended" schema is now the default in roundup init. +# . The schemas have had their page headings modified to cope with the new +# login handling. Existing installations should copy the interfaces.py +# file from the roundup lib directory to their instance home. +# . Incorrectly had a Bizar Software copyright on the cgitb.py module from +# Ping - has been removed. +# . Fixed a whole bunch of places in the CGI interface where we should have +# been returning Not Found instead of throwing an exception. +# . Fixed a deviation from the spec: trying to modify the 'id' property of +# an item now throws an exception. +# +# Revision 1.12 2001/09/29 13:27:00 richard +# CGI interfaces now spit up a top-level index of all the instances they can +# serve. +# +# Revision 1.11 2001/08/07 00:24:42 richard +# stupid typo +# +# Revision 1.10 2001/08/07 00:15:51 richard +# Added the copyright/license notice to (nearly) all files at request of +# Bizar Software. +# +# Revision 1.9 2001/08/05 07:44:36 richard +# Instances are now opened by a special function that generates a unique +# module name for the instances on import time. +# # Revision 1.8 2001/08/03 01:28:33 richard # Used the much nicer load_package, pointed out by Steve Majewski. #