Code

moving the bin files to facilitate out-of-the-boxness
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Mon, 23 Jul 2001 03:46:48 +0000 (03:46 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Mon, 23 Jul 2001 03:46:48 +0000 (03:46 +0000)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@31 57a73879-2fb5-44c3-a270-3262357dd7e2

bin/roundup [deleted file]
bin/roundup-mailgw [deleted file]
bin/roundup-server [deleted file]
roundup-admin [new file with mode: 0755]
roundup-mailgw [new file with mode: 0755]
roundup-server [new file with mode: 0755]

diff --git a/bin/roundup b/bin/roundup
deleted file mode 100755 (executable)
index 0ca6554..0000000
+++ /dev/null
@@ -1,271 +0,0 @@
-#! /usr/bin/python
-
-# $Id: roundup,v 1.1 2001-07-22 11:15:45 richard Exp $
-
-import sys
-if int(sys.version[0]) < 2:
-    print 'Roundup requires python 2.0 or later.'
-    sys.exit(1)
-
-import string, os, getpass
-from roundup import date, roundupdb, init
-
-def determineLogin(instance, argv, n = 2):
-    name = password = ''
-    if argv[2] == '-u':
-        l = argv[3].split(':')
-        name = l[0]
-        if len(l) > 1:
-            password = l[1]
-        n = 4
-    elif os.environ.has_key('ROUNDUP_LOGIN'):
-        l = os.environ['ROUNDUP_LOGIN'].split(':')
-        name = l[0]
-        if len(l) > 1:
-            password = l[1]
-    while not name:
-        name = raw_input('Login name: ')
-    while not password:
-        password = getpass.getpass('  password: ')
-    # TODO use the password...
-    return n, instance.open(name)
-
-def usage(message=''):
-    if message: message = 'Problem: '+message+'\n'
-    print '''%sUsage:
-
- roundup [-i instance] init template
-   -- initialise the database
- roundup [-i instance] spec classname
-   -- show the properties for a classname
- roundup [-i instance] create [-u login] classname propname=value ...
-   -- create a new entry of a given class
- roundup [-i instance] list [-c] classname
-   -- list the instances of a class
- roundup [-i instance] history [-c] designator
-   -- show the history entries of a designator
- roundup [-i instance] get [-c] designator[,designator,...] propname
-   -- get the given property of one or more designator(s)
- roundup [-i instance] set [-u login] designator[,designator,...] propname=value ...
-   -- set the given property of one or more designator(s)
- roundup [-i instance] find [-c] classname propname=value ...
-   -- find the class instances with a given property
- roundup [-i instance] retire designator[,designator,...]
-   -- "retire" a designator
- roundup help    
-   -- this help
- roundup morehelp
-   -- even more detailed help
-'''%message
-
-def moreusage(message=''):
-    usage(message)
-    print '''
-All commands (except help) require an instance specifier. This is just the path
-to the roundup instance you're working with. It may be specified in the environment
-variable ROUNDUP_INSTANCE or on the command line as "-i instance".
-
-A designator is a classname and a nodeid concatenated, eg. bug1, user10, ...
-
-Property values are represented as strings in command arguments and in the
-printed results:
- . Strings are, well, strings.
- . Date values are printed in the full date format in the local time zone, and
-   accepted in the full format or any of the partial formats explained below.
- . Link values are printed as node designators. When given as an argument,
-   node designators and key strings are both accepted.
- . Multilink values are printed as lists of node designators joined by commas.
-   When given as an argument, node designators and key strings are both
-   accepted; an empty string, a single node, or a list of nodes joined by
-   commas is accepted.
-
-When multiple nodes are specified to the roundup get or roundup set
-commands, the specified properties are retrieved or set on all the listed
-nodes. 
-
-When multiple results are returned by the roundup get or roundup find
-commands, they are printed one per line (default) or joined by commas (with
-the -c) option. 
-
-Where the command changes data, a login name/password is required. The
-login may be specified as either "name" or "name:password".
- . ROUNDUP_LOGIN environment variable
- . the -u command-line option
-If either the name or password is not supplied, they are obtained from the
-command-line. 
-
-Date format examples:
-  "2000-04-17.03:45" means <Date 2000-04-17.08:45:00>
-  "2000-04-17" means <Date 2000-04-17.00:00:00>
-  "01-25" means <Date yyyy-01-25.00:00:00>
-  "08-13.22:13" means <Date yyyy-08-14.03:13:00>
-  "11-07.09:32:43" means <Date yyyy-11-07.14:32:43>
-  "14:25" means <Date yyyy-mm-dd.19:25:00>
-  "8:47:11" means <Date yyyy-mm-dd.13:47:11>
-  "." means "right now"
-'''
-
-def main():
-    argv = sys.argv
-
-    if len(argv) == 1:
-        usage('No command specified')
-        return 1
-
-    # handle help now
-    if argv[1] == 'help':
-        usage()
-        return 0
-    if argv[1] == 'morehelp':
-        moreusage()
-        return 0
-
-    # figure the instance home
-    n = 1
-    if argv[1] == '-i':
-        if len(argv) == 2:
-            usage()
-            return 1
-        instance_home = argv[2]
-        n = 3
-    else:
-        instance_home = os.environ.get('ROUNDUP_INSTANCE', '')
-    if not instance_home:
-        usage('No instance home specified')
-        return 1
-
-    # now figure the command
-    command = argv[n]
-    n = n + 1
-
-    if command == 'init':
-        adminpw = ''
-        confirm = 'x'
-        while adminpw != confirm:
-            adminpw = getpass.getpass('Admin Password:')
-            confirm = getpass.getpass('       Confirm:')
-        init.init(instance_home, argv[n],  adminpw)
-        return 0
-
-    # get the instance
-    path, instance = os.path.split(instance_home)
-    sys.path.insert(0, path)
-    try:
-        instance = __import__(instance)
-    finally:
-        del sys.path[0]
-
-    if command == 'get':
-        db = instance.open()
-        designators = string.split(argv[n], ',')
-        propname = argv[n+1]
-        # TODO: handle the -c option
-        for designator in designators:
-            classname, nodeid = roundupdb.splitDesignator(designator)
-            print db.getclass(classname).get(nodeid, propname)
-
-    elif command == 'set':
-        n, db = determineLogin(instance, argv, n)
-        designators = string.split(argv[n], ',')
-        props = {}
-        for prop in argv[n+1:]:
-            key, value = prop.split('=')
-            props[key] = value
-        for designator in designators:
-            classname, nodeid = roundupdb.splitDesignator(designator)
-            cl = db.getclass(classname)
-            properties = cl.getprops()
-            for key, value in props.items():
-                type =  properties[key]
-                if type.isStringType:
-                    continue
-                elif type.isDateType:
-                    props[key] = date.Date(value)
-                elif type.isIntervalType:
-                    props[key] = date.Interval(value)
-                elif type.isLinkType:
-                    props[key] = value
-                elif type.isMultilinkType:
-                    props[key] = value.split(',')
-            apply(cl.set, (nodeid, ), props)
-
-    elif command == 'find':
-        db = instance.open()
-        classname = argv[n]
-        cl = db.getclass(classname)
-
-        # look up the linked-to class and get the nodeid that has the value
-        propname, value = argv[n+1:].split('=')
-        propcl = cl[propname].classname
-        nodeid = propcl.lookup(value)
-
-        # now do the find
-        # TODO: handle the -c option
-        print cl.find(propname, nodeid)
-
-    elif command == 'spec':
-        db = instance.open()
-        classname = argv[n]
-        cl = db.getclass(classname)
-        for key, value in cl.properties.items():
-            print '%s: %s'%(key, value)
-
-    elif command == 'create':
-        n, db = determineLogin(instance, argv, n)
-        classname = argv[n]
-        cl = db.getclass(classname)
-        props = {}
-        properties = cl.getprops()
-        for prop in argv[n+1:]:
-            key, value = prop.split('=')
-            type =  properties[key]
-            if type.isStringType:
-                props[key] = value 
-            elif type.isDateType:
-                props[key] = date.Date(value)
-            elif type.isIntervalType:
-                props[key] = date.Interval(value)
-            elif type.isLinkType:
-                props[key] = value
-            elif type.isMultilinkType:
-                props[key] = value.split(',')
-        print apply(cl.create, (), props)
-
-    elif command == 'list':
-        db = instance.open()
-        classname = argv[n]
-        cl = db.getclass(classname)
-        key = cl.getkey() or cl.properties.keys()[0]
-        # TODO: handle the -c option
-        for nodeid in cl.list():
-            value = cl.get(nodeid, key)
-            print "%4s: %s"%(nodeid, value)
-
-    elif command == 'history':
-        db = instance.open()
-        classname, nodeid = roundupdb.splitDesignator(argv[n])
-        # TODO: handle the -c option
-        print db.getclass(classname).history(nodeid)
-
-    elif command == 'retire':
-        n, db = determineLogin(instance, argv, n)
-        designators = string.split(argv[n], ',')
-        for designator in designators:
-            classname, nodeid = roundupdb.splitDesignator(designator)
-            db.getclass(classname).retire(nodeid)
-
-    else:
-        print "Unknown command '%s'"%command
-        usage()
-        return 1
-
-    db.close()
-    return 0
-
-if __name__ == '__main__':
-    sys.exit(main())
-
-#
-# $Log: not supported by cvs2svn $
-#
-
diff --git a/bin/roundup-mailgw b/bin/roundup-mailgw
deleted file mode 100755 (executable)
index 4add812..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#! /usr/bin/python
-
-# $ID: $
-
-import sys
-if int(sys.version[0]) < 2:
-    print "Roundup requires Python 2.0 or newer."
-    sys.exit(1)
-
-# figure the instance home
-import os
-if len(sys.argv) > 1:
-    instance_home = sys.argv[1]
-else:
-    instance_home = os.environ.get('ROUNDUP_INSTANCE', '')
-if not instance_home:
-    print 'No instance home specified'
-    sys.exit(1)
-
-# get the instance
-path, instance = os.path.split(instance_home)
-sys.path.insert(0, path)
-instance = __import__(instance)
-sys.path[0]
-
-# invokde the mail handler
-db = instance.open('admin')
-handler = instance.MailGW(db)
-handler.main(sys.stdin)
-
-#
-# $Log: not supported by cvs2svn $
-#
-
diff --git a/bin/roundup-server b/bin/roundup-server
deleted file mode 100755 (executable)
index c1e12c1..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-#!/usr/bin/python
-""" HTTP Server that serves roundup.
-
-Stolen from CGIHTTPServer
-
-$Id: roundup-server,v 1.1 2001-07-22 11:15:45 richard Exp $
-
-"""
-import sys
-if int(sys.version[0]) < 2:
-    print "Content-Type: text/plain\n"
-    print "Roundup requires Python 2.0 or newer."
-
-__version__ = "0.1"
-
-__all__ = ["RoundupRequestHandler"]
-
-import os, urllib, StringIO, traceback, cgi, binascii, string
-import BaseHTTPServer
-import SimpleHTTPServer
-
-# Roundup modules of use here
-from roundup import cgitb, cgi_client
-
-# These are here temporarily until I get a real reload system in place
-from roundup import date, hyperdb, hyper_bsddb, roundupdb, htmltemplate
-
-#
-##  Configuration
-#
-
-# This indicates where the Roundup instance lives
-ROUNDUPS = {
-    'test': '/tmp/roundup_test',
-}
-
-# 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 DevNull:
-#    def write(self, info):
-#        pass
-#LOG = open('/var/log/roundup.cgi.log', 'a')
-#LOG = DevNull()
-
-#
-##  end configuration
-#
-
-
-class RoundupRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
-    def send_head(self):
-        """Version of send_head that support CGI scripts"""
-        # TODO: actually do the HEAD ...
-        return self.run_cgi()
-
-    def run_cgi(self):
-        """ Execute the CGI command. Wrap an innner call in an error
-            handler so all errors can be caught.
-        """
-        save_stdin = sys.stdin
-        sys.stdin = self.rfile
-        try:
-            self.inner_run_cgi()
-        except cgi_client.Unauthorised:
-            self.wfile.write('Content-Type: text/html\n')
-            self.wfile.write('Status: 403\n')
-            self.wfile.write('Unauthorised')
-        except:
-            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(cgi.escape(s.getvalue()))
-                self.wfile.write("</pre>\n")
-        sys.stdin = save_stdin
-
-    def inner_run_cgi(self):
-        ''' This is the inner part of the CGI handling
-        '''
-
-        rest = self.path
-        i = rest.rfind('?')
-        if i >= 0:
-            rest, query = rest[:i], rest[i+1:]
-        else:
-            query = ''
-
-        # figure the instance
-        if rest == '/':
-            raise ValueError, 'No instance specified'
-        l_path = string.split(rest, '/')
-        instance = urllib.unquote(l_path[1])
-        if ROUNDUPS.has_key(instance):
-            instance_home = ROUNDUPS[instance]
-            module_path, instance = os.path.split(instance_home)
-            sys.path.insert(0, module_path)
-            try:
-                instance = __import__(instance)
-            finally:
-                del sys.path[0]
-        else:
-            raise ValueError, 'No such instance "%s"'%instance
-
-        # figure out what the rest of the path is
-        if len(l_path) > 2:
-            rest = '/'.join(l_path[2:])
-        else:
-            rest = '/'
-
-        # Set up the CGI environment
-        env = {}
-        env['REQUEST_METHOD'] = self.command
-        env['PATH_INFO'] = urllib.unquote(rest)
-        if query:
-            env['QUERY_STRING'] = query
-        host = self.address_string()
-        if self.headers.typeheader is None:
-            env['CONTENT_TYPE'] = self.headers.type
-        else:
-            env['CONTENT_TYPE'] = self.headers.typeheader
-        length = self.headers.getheader('content-length')
-        if length:
-            env['CONTENT_LENGTH'] = length
-        co = filter(None, self.headers.getheaders('cookie'))
-        if co:
-            env['HTTP_COOKIE'] = ', '.join(co)
-        env['SCRIPT_NAME'] = ''
-        env['SERVER_NAME'] = self.server.server_name
-        env['SERVER_PORT'] = str(self.server.server_port)
-
-        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.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
-
-if __name__ == '__main__':
-    # TODO make this configurable again? command-line seems ok to me...
-    address = ('', 8080)
-    httpd = BaseHTTPServer.HTTPServer(address, RoundupRequestHandler)
-    print 'Roundup server started on', address
-    httpd.serve_forever()
-
-#
-# $Log: not supported by cvs2svn $
-#
-
diff --git a/roundup-admin b/roundup-admin
new file mode 100755 (executable)
index 0000000..1068301
--- /dev/null
@@ -0,0 +1,274 @@
+#! /usr/bin/python
+
+# $Id: roundup-admin,v 1.1 2001-07-23 03:46:48 richard Exp $
+
+import sys
+if int(sys.version[0]) < 2:
+    print 'Roundup requires python 2.0 or later.'
+    sys.exit(1)
+
+import string, os, getpass
+from roundup import date, roundupdb, init
+
+def determineLogin(instance, argv, n = 2):
+    name = password = ''
+    if argv[2] == '-u':
+        l = argv[3].split(':')
+        name = l[0]
+        if len(l) > 1:
+            password = l[1]
+        n = 4
+    elif os.environ.has_key('ROUNDUP_LOGIN'):
+        l = os.environ['ROUNDUP_LOGIN'].split(':')
+        name = l[0]
+        if len(l) > 1:
+            password = l[1]
+    while not name:
+        name = raw_input('Login name: ')
+    while not password:
+        password = getpass.getpass('  password: ')
+    # TODO use the password...
+    return n, instance.open(name)
+
+def usage(message=''):
+    if message: message = 'Problem: '+message+'\n'
+    print '''%sUsage:
+
+ roundup [-i instance] init template
+   -- initialise the database
+ roundup [-i instance] spec classname
+   -- show the properties for a classname
+ roundup [-i instance] create [-u login] classname propname=value ...
+   -- create a new entry of a given class
+ roundup [-i instance] list [-c] classname
+   -- list the instances of a class
+ roundup [-i instance] history [-c] designator
+   -- show the history entries of a designator
+ roundup [-i instance] get [-c] designator[,designator,...] propname
+   -- get the given property of one or more designator(s)
+ roundup [-i instance] set [-u login] designator[,designator,...] propname=value ...
+   -- set the given property of one or more designator(s)
+ roundup [-i instance] find [-c] classname propname=value ...
+   -- find the class instances with a given property
+ roundup [-i instance] retire designator[,designator,...]
+   -- "retire" a designator
+ roundup help    
+   -- this help
+ roundup morehelp
+   -- even more detailed help
+'''%message
+
+def moreusage(message=''):
+    usage(message)
+    print '''
+All commands (except help) require an instance specifier. This is just the path
+to the roundup instance you're working with. It may be specified in the environment
+variable ROUNDUP_INSTANCE or on the command line as "-i instance".
+
+A designator is a classname and a nodeid concatenated, eg. bug1, user10, ...
+
+Property values are represented as strings in command arguments and in the
+printed results:
+ . Strings are, well, strings.
+ . Date values are printed in the full date format in the local time zone, and
+   accepted in the full format or any of the partial formats explained below.
+ . Link values are printed as node designators. When given as an argument,
+   node designators and key strings are both accepted.
+ . Multilink values are printed as lists of node designators joined by commas.
+   When given as an argument, node designators and key strings are both
+   accepted; an empty string, a single node, or a list of nodes joined by
+   commas is accepted.
+
+When multiple nodes are specified to the roundup get or roundup set
+commands, the specified properties are retrieved or set on all the listed
+nodes. 
+
+When multiple results are returned by the roundup get or roundup find
+commands, they are printed one per line (default) or joined by commas (with
+the -c) option. 
+
+Where the command changes data, a login name/password is required. The
+login may be specified as either "name" or "name:password".
+ . ROUNDUP_LOGIN environment variable
+ . the -u command-line option
+If either the name or password is not supplied, they are obtained from the
+command-line. 
+
+Date format examples:
+  "2000-04-17.03:45" means <Date 2000-04-17.08:45:00>
+  "2000-04-17" means <Date 2000-04-17.00:00:00>
+  "01-25" means <Date yyyy-01-25.00:00:00>
+  "08-13.22:13" means <Date yyyy-08-14.03:13:00>
+  "11-07.09:32:43" means <Date yyyy-11-07.14:32:43>
+  "14:25" means <Date yyyy-mm-dd.19:25:00>
+  "8:47:11" means <Date yyyy-mm-dd.13:47:11>
+  "." means "right now"
+'''
+
+def main():
+    argv = sys.argv
+
+    if len(argv) == 1:
+        usage('No command specified')
+        return 1
+
+    # handle help now
+    if argv[1] == 'help':
+        usage()
+        return 0
+    if argv[1] == 'morehelp':
+        moreusage()
+        return 0
+
+    # figure the instance home
+    n = 1
+    if argv[1] == '-i':
+        if len(argv) == 2:
+            usage()
+            return 1
+        instance_home = argv[2]
+        n = 3
+    else:
+        instance_home = os.environ.get('ROUNDUP_INSTANCE', '')
+    if not instance_home:
+        usage('No instance home specified')
+        return 1
+
+    # now figure the command
+    command = argv[n]
+    n = n + 1
+
+    if command == 'init':
+        adminpw = ''
+        confirm = 'x'
+        while adminpw != confirm:
+            adminpw = getpass.getpass('Admin Password:')
+            confirm = getpass.getpass('       Confirm:')
+        init.init(instance_home, argv[n],  adminpw)
+        return 0
+
+    # get the instance
+    path, instance = os.path.split(instance_home)
+    sys.path.insert(0, path)
+    try:
+        instance = __import__(instance)
+    finally:
+        del sys.path[0]
+
+    if command == 'get':
+        db = instance.open()
+        designators = string.split(argv[n], ',')
+        propname = argv[n+1]
+        # TODO: handle the -c option
+        for designator in designators:
+            classname, nodeid = roundupdb.splitDesignator(designator)
+            print db.getclass(classname).get(nodeid, propname)
+
+    elif command == 'set':
+        n, db = determineLogin(instance, argv, n)
+        designators = string.split(argv[n], ',')
+        props = {}
+        for prop in argv[n+1:]:
+            key, value = prop.split('=')
+            props[key] = value
+        for designator in designators:
+            classname, nodeid = roundupdb.splitDesignator(designator)
+            cl = db.getclass(classname)
+            properties = cl.getprops()
+            for key, value in props.items():
+                type =  properties[key]
+                if type.isStringType:
+                    continue
+                elif type.isDateType:
+                    props[key] = date.Date(value)
+                elif type.isIntervalType:
+                    props[key] = date.Interval(value)
+                elif type.isLinkType:
+                    props[key] = value
+                elif type.isMultilinkType:
+                    props[key] = value.split(',')
+            apply(cl.set, (nodeid, ), props)
+
+    elif command == 'find':
+        db = instance.open()
+        classname = argv[n]
+        cl = db.getclass(classname)
+
+        # look up the linked-to class and get the nodeid that has the value
+        propname, value = argv[n+1:].split('=')
+        propcl = cl[propname].classname
+        nodeid = propcl.lookup(value)
+
+        # now do the find
+        # TODO: handle the -c option
+        print cl.find(propname, nodeid)
+
+    elif command == 'spec':
+        db = instance.open()
+        classname = argv[n]
+        cl = db.getclass(classname)
+        for key, value in cl.properties.items():
+            print '%s: %s'%(key, value)
+
+    elif command == 'create':
+        n, db = determineLogin(instance, argv, n)
+        classname = argv[n]
+        cl = db.getclass(classname)
+        props = {}
+        properties = cl.getprops()
+        for prop in argv[n+1:]:
+            key, value = prop.split('=')
+            type =  properties[key]
+            if type.isStringType:
+                props[key] = value 
+            elif type.isDateType:
+                props[key] = date.Date(value)
+            elif type.isIntervalType:
+                props[key] = date.Interval(value)
+            elif type.isLinkType:
+                props[key] = value
+            elif type.isMultilinkType:
+                props[key] = value.split(',')
+        print apply(cl.create, (), props)
+
+    elif command == 'list':
+        db = instance.open()
+        classname = argv[n]
+        cl = db.getclass(classname)
+        key = cl.getkey() or cl.properties.keys()[0]
+        # TODO: handle the -c option
+        for nodeid in cl.list():
+            value = cl.get(nodeid, key)
+            print "%4s: %s"%(nodeid, value)
+
+    elif command == 'history':
+        db = instance.open()
+        classname, nodeid = roundupdb.splitDesignator(argv[n])
+        # TODO: handle the -c option
+        print db.getclass(classname).history(nodeid)
+
+    elif command == 'retire':
+        n, db = determineLogin(instance, argv, n)
+        designators = string.split(argv[n], ',')
+        for designator in designators:
+            classname, nodeid = roundupdb.splitDesignator(designator)
+            db.getclass(classname).retire(nodeid)
+
+    else:
+        print "Unknown command '%s'"%command
+        usage()
+        return 1
+
+    db.close()
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())
+
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.1  2001/07/22 11:15:45  richard
+# More Grande Splite stuff
+#
+#
+
diff --git a/roundup-mailgw b/roundup-mailgw
new file mode 100755 (executable)
index 0000000..c16e38e
--- /dev/null
@@ -0,0 +1,37 @@
+#! /usr/bin/python
+
+# $ID: $
+
+import sys
+if int(sys.version[0]) < 2:
+    print "Roundup requires Python 2.0 or newer."
+    sys.exit(1)
+
+# figure the instance home
+import os
+if len(sys.argv) > 1:
+    instance_home = sys.argv[1]
+else:
+    instance_home = os.environ.get('ROUNDUP_INSTANCE', '')
+if not instance_home:
+    print 'No instance home specified'
+    sys.exit(1)
+
+# get the instance
+path, instance = os.path.split(instance_home)
+sys.path.insert(0, path)
+instance = __import__(instance)
+sys.path[0]
+
+# invokde the mail handler
+db = instance.open('admin')
+handler = instance.MailGW(db)
+handler.main(sys.stdin)
+
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.1  2001/07/22 11:15:45  richard
+# More Grande Splite stuff
+#
+#
+
diff --git a/roundup-server b/roundup-server
new file mode 100755 (executable)
index 0000000..ff4e0c1
--- /dev/null
@@ -0,0 +1,226 @@
+#!/usr/bin/python
+""" HTTP Server that serves roundup.
+
+Stolen from CGIHTTPServer
+
+$Id: roundup-server,v 1.1 2001-07-23 03:46:48 richard Exp $
+
+"""
+import sys
+if int(sys.version[0]) < 2:
+    print "Content-Type: text/plain\n"
+    print "Roundup requires Python 2.0 or newer."
+
+__version__ = "0.1"
+
+__all__ = ["RoundupRequestHandler"]
+
+import os, urllib, StringIO, traceback, cgi, binascii, string
+import BaseHTTPServer
+import SimpleHTTPServer
+
+# Roundup modules of use here
+from roundup import cgitb, cgi_client
+
+# These are here temporarily until I get a real reload system in place
+from roundup import date, hyperdb, hyper_bsddb, roundupdb, htmltemplate
+
+#
+##  Configuration
+#
+
+# This indicates where the Roundup instance lives
+ROUNDUPS = {
+    'test': '/tmp/roundup_test',
+}
+
+# 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 DevNull:
+#    def write(self, info):
+#        pass
+#LOG = open('/var/log/roundup.cgi.log', 'a')
+#LOG = DevNull()
+
+#
+##  end configuration
+#
+
+
+class RoundupRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+    def send_head(self):
+        """Version of send_head that support CGI scripts"""
+        # TODO: actually do the HEAD ...
+        return self.run_cgi()
+
+    def run_cgi(self):
+        """ Execute the CGI command. Wrap an innner call in an error
+            handler so all errors can be caught.
+        """
+        save_stdin = sys.stdin
+        sys.stdin = self.rfile
+        try:
+            self.inner_run_cgi()
+        except cgi_client.Unauthorised:
+            self.wfile.write('Content-Type: text/html\n')
+            self.wfile.write('Status: 403\n')
+            self.wfile.write('Unauthorised')
+        except:
+            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(cgi.escape(s.getvalue()))
+                self.wfile.write("</pre>\n")
+        sys.stdin = save_stdin
+
+    def inner_run_cgi(self):
+        ''' This is the inner part of the CGI handling
+        '''
+
+        rest = self.path
+        i = rest.rfind('?')
+        if i >= 0:
+            rest, query = rest[:i], rest[i+1:]
+        else:
+            query = ''
+
+        # figure the instance
+        if rest == '/':
+            raise ValueError, 'No instance specified'
+        l_path = string.split(rest, '/')
+        instance = urllib.unquote(l_path[1])
+        if ROUNDUPS.has_key(instance):
+            instance_home = ROUNDUPS[instance]
+            module_path, instance = os.path.split(instance_home)
+            sys.path.insert(0, module_path)
+            try:
+                instance = __import__(instance)
+            finally:
+                del sys.path[0]
+        else:
+            raise ValueError, 'No such instance "%s"'%instance
+
+        # figure out what the rest of the path is
+        if len(l_path) > 2:
+            rest = '/'.join(l_path[2:])
+        else:
+            rest = '/'
+
+        # Set up the CGI environment
+        env = {}
+        env['REQUEST_METHOD'] = self.command
+        env['PATH_INFO'] = urllib.unquote(rest)
+        if query:
+            env['QUERY_STRING'] = query
+        host = self.address_string()
+        if self.headers.typeheader is None:
+            env['CONTENT_TYPE'] = self.headers.type
+        else:
+            env['CONTENT_TYPE'] = self.headers.typeheader
+        length = self.headers.getheader('content-length')
+        if length:
+            env['CONTENT_LENGTH'] = length
+        co = filter(None, self.headers.getheaders('cookie'))
+        if co:
+            env['HTTP_COOKIE'] = ', '.join(co)
+        env['SCRIPT_NAME'] = ''
+        env['SERVER_NAME'] = self.server.server_name
+        env['SERVER_PORT'] = str(self.server.server_port)
+
+        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.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
+
+if __name__ == '__main__':
+    # TODO make this configurable again? command-line seems ok to me...
+    address = ('', 8080)
+    httpd = BaseHTTPServer.HTTPServer(address, RoundupRequestHandler)
+    print 'Roundup server started on', address
+    httpd.serve_forever()
+
+#
+# $Log: not supported by cvs2svn $
+# Revision 1.1  2001/07/22 11:15:45  richard
+# More Grande Splite stuff
+#
+#
+