summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 691a4ff)
raw | patch | inline | side by side (parent: 691a4ff)
author | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Sun, 22 Jul 2001 11:15:45 +0000 (11:15 +0000) | ||
committer | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Sun, 22 Jul 2001 11:15:45 +0000 (11:15 +0000) |
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@27 57a73879-2fb5-44c3-a270-3262357dd7e2
bin/roundup | [new file with mode: 0755] | patch | blob |
bin/roundup-mailgw | [new file with mode: 0755] | patch | blob |
bin/roundup-server | [new file with mode: 0755] | patch | blob |
diff --git a/bin/roundup b/bin/roundup
--- /dev/null
+++ b/bin/roundup
@@ -0,0 +1,271 @@
+#! /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
--- /dev/null
+++ b/bin/roundup-mailgw
@@ -0,0 +1,34 @@
+#! /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
--- /dev/null
+++ b/bin/roundup-server
@@ -0,0 +1,223 @@
+#!/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 $
+#
+