diff --git a/roundup-server b/roundup-server
index 9b96638a97f122714b3a0d240ea24bfccb5d6fbc..5c8222c601d6e1137ef7701c9bf3395251dc7f09 100755 (executable)
--- a/roundup-server
+++ b/roundup-server
#!/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
'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
#
-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
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("<pre>")
s = StringIO.StringIO()
traceback.print_exc(None, s)
self.wfile.write("</pre>\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(_('<html><head><title>Roundup instances index</title></head>\n'))
+ w(_('<body><h1>Roundup instances index</h1><ol>\n'))
+ for instance in self.ROUNDUP_INSTANCE_HOMES.keys():
+ w(_('<li><a href="%(instance_url)s/index">%(instance_name)s</a>\n')%{
+ 'instance_url': urllib.quote(instance),
+ 'instance_name': cgi.escape(instance)})
+ w(_('</ol></body></html>'))
+
def inner_run_cgi(self):
''' This is the inner part of the CGI handling
'''
# 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:
# Set up the CGI environment
env = {}
+ env['INSTANCE_NAME'] = instance_name
env['REQUEST_METHOD'] = self.command
env['PATH_INFO'] = urllib.unquote(rest)
if query:
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
"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():
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__':
#
# $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.
#