[Zope-CVS] CVS: Zope27 - setup.py:1.1.2.1 test.py:1.1.2.1 wo_pcgi.py:1.1.2.1 z2.py:1.1.2.1 zpasswd.py:1.1.2.1

Jim Fulton jim@zope.com
Tue, 13 Aug 2002 15:33:28 -0400


Update of /cvs-repository/Zope27
In directory cvs.zope.org:/tmp/cvs-serv30914

Added Files:
      Tag: Zope-2_7-development-branch
	setup.py test.py wo_pcgi.py z2.py zpasswd.py 
Log Message:
Added some extra needed files

=== Added File Zope27/setup.py === (1015/1115 lines abridged)
#! /usr/bin/env python
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################

"""
Distutils setup for Zope

  In-place building

    This builds extension modules in-place, much like build_extensions.py
    does.  Use 'setup.py' like this::

      python setup.py build_ext -i

  Installation

    This builds extension modules, compiles python modules, and installs
    everything needed to support Zope instances in the directory of
    your choosing.  For example, to use '/usr/local/lib/zope'::

      python setup.py install \
        --home=/usr/local/lib/zope \
        --install-platlib=/usr/local/lib/zope \
        --install-purelib=/usr/local/lib/zope

    Note that with this method, all packages and scripts (including
    ZServer and z2.py) go in the same directory as Zope modules, which
    are distributed in lib/python.  You will need to set both ZOPE_HOME
    and SOFTWARE_HOME to point to your destination directory in order
    for Zope to work in this configuration.
"""

import os
import sys

from distutils.core import setup as distutils_setup
from distutils.extension import Extension

# This function collects setup information for one massive distutils
# run to be done at the end of the script.  If you're making a setup.py

[-=- -=- -=- 1015 lines omitted -=- -=- -=-]

#                 sources=['ZServer/medusa/sendfile/sendfilemodule.c'])]

)

# z2.py
setup(
    name='z2.py',
    author=AUTHOR,

    py_modules=['z2']
)

# zpasswd
setup(
    name='zpasswd',
    author=AUTHOR,

    py_modules=['zpasswd']
)

# Default imports
setup(
    name='Zope default imports',
    author=AUTHOR,

    data_files=[['import', ['import/*.zexp']]],
    cmdclass={'install_data': install_data}
)

# And now, the root-level stuff

distutils_setup(
    name='Zope',
    author=AUTHOR,

    packages=setup_info.get('packages', []),
    data_files=setup_info.get('data_files', []),

    headers=setup_info.get('headers', []),
    ext_modules=setup_info.get('ext_modules', []),

    cmdclass={'install': install, 'install_data': install_data}
)
distutils_setup(
    name='Zope',
    author=AUTHOR,

    py_modules=setup_info.get('py_modules', []),
    cmdclass={'install': install, 'install_data': install_data}
)


=== Added File Zope27/test.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
test.py [-bdvvL] [modfilter [testfilter]]

Test harness.

-b  build
    Run "python setup.py -q build_ext -i" before running tests,
    where "python" is the version of python used to run test.py.
    Highly recommended.

-d  debug
    Instead of the normal test harness, run a debug version which
    doesn't catch any exceptions.  This is occasionally handy when the
    unittest code catching the exception doesn't work right.
    Unfortunately, the debug harness doesn't print the name of the
    test, so Use With Care.

-v  verbose
    With one -v, unittest prints a dot (".") for each test run.  With
    -vv, unittest prints the name of each test (for some definition of
    "name" ...).  Witn no -v, unittest is silent until the end of the
    run, except when errors occur.

-L  Loop
    Keep running the selected tests in a loop.  You may experience
    memory leakage.

-g  threshold
    Set the garbage collector generation0 threshold.  This can be used to
    stress memory and gc correctness.  Some crashes are only reproducible when
    the threshold is set to 1 (agressive garbage collection).  Do "-g 0" to
    disable garbage collection altogether.

-G flag
    Set garbage collector debug flag.  The flag argument should be the name
    of a DEBUG_ attribute of the gc module.  This argument can be repeated.

-u  Use unittestgui
-m  Use unittestgui; start minimized
    Use the PyUnit GUI instead of output to the command line. The GUI
    imports tests on its own, taking care to reload all dependencies on each
    run. The debug (-d), verbose (-v), and Loop (-L) options will be
    ignored. The testfilter filter is also not applied.

modfilter
testfilter
    Case-sensitive regexps to limit which tests are run, used in search
    (not match) mode.
    In an extension of Python regexp notation, a leading "!" is stripped
    and causes the sense of the remaining regexp to be negated (so "!bc"
    matches any string that does not match "bc", and vice versa).
    By default these act like ".", i.e. nothing is excluded.

    modfilter is applied to a test file's path, starting at "build" and
    including (OS-dependent) path separators.

    testfilter is applied to the (method) name of the unittest methods
    contained in the test files whose paths modfilter matched.

Extreme (yet useful) examples:

    test.py -vvb . "^checkWriteClient$"

    Builds the project silently, then runs unittest in verbose mode on all
    tests whose names are precisely "checkWriteClient".  Useful when
    debugging a specific test.

    test.py -vvb . "!^checkWriteClient$"

    As before, but runs all tests whose names aren't precisely
    "checkWriteClient".  Useful to avoid a specific failing test you don't
    want to deal with just yet.

    test.py -m . "!^checkWriteClient$"

    As before, but now opens up a minimized PyUnit GUI window (only showing
    the progressbar). Double-clicking the progressbar will start the import
    and run all tests. Useful for refactoring runs where you continually
    want to make sure all tests still pass.
"""

import os
import re
import sys
import traceback
import unittest
import linecache
from os.path import join

from distutils.util import get_platform

# We know we're going to need this so import it now.  Python 2.2 does not come
# with the pyexpat library by default, although Python 2.3 will.
try:
    import pyexpat
except ImportError:
    print >> sys.stderr, "WARNING: the pyexpat module is required"
    raise

class ImmediateTestResult(unittest._TextTestResult):

    __super_init = unittest._TextTestResult.__init__

    def __init__(self, *args, **kwarg):
        debug = kwarg.get('debug')
        if debug is not None:
            del kwarg['debug']
        self.__super_init(*args, **kwarg)
        self._debug = debug
        
    def _print_traceback(self, msg, err, test, errlist):
        if self.showAll or self.dots:
            self.stream.writeln("\n")

        tb = ''.join(traceback.format_exception(*err))
        self.stream.writeln(msg)
        self.stream.writeln(tb)
        errlist.append((test, tb))

    def addError(self, test, err):
        if self._debug:
            raise err[0], err[1], err[2]
        self._print_traceback("Error in test %s" % test, err,
                              test, self.errors)

    def addFailure(self, test, err):
        if self._debug:
            raise err[0], err[1], err[2]
        self._print_traceback("Failure in test %s" % test, err,
                              test, self.failures)

    def printErrorList(self, flavor, errors):
        for test, err in errors:
            self.stream.writeln(self.separator1)
            self.stream.writeln("%s: %s" % (flavor, self.getDescription(test)))
            self.stream.writeln(self.separator2)
            self.stream.writeln(err)

class ImmediateTestRunner(unittest.TextTestRunner):

    __super_init = unittest.TextTestRunner.__init__

    def __init__(self, **kwarg):
        debug = kwarg.get('debug')
        if debug is not None:
            del kwarg['debug']
        self.__super_init(**kwarg)
        self._debug = debug

    def _makeResult(self):
        return ImmediateTestResult(self.stream, self.descriptions,
                                   self.verbosity, debug=self._debug)

# setup list of directories to put on the path

def setup_path():
    DIRS = [join('lib','python'),
            ]
    cwd = os.getcwd()
    for d in DIRS:
        sys.path.insert(0, join(cwd, d))

def match(rx, s):
    if not rx:
        return 1
    if rx[0] == '!':
        return re.search(rx[1:], s) is None
    else:
        return re.search(rx, s) is not None

class TestFileFinder:
    def __init__(self):
        self.files = []

    def visit(self, rx, dir, files):
        if dir[-5:] != "tests":
            return
        # ignore tests that aren't in packages
        if not "__init__.py" in files:
            if not files or files == ['CVS']:
                return

            print "not a package", dir
            return

        for file in files:
            if file[:4] == "test" and file[-3:] == ".py":
                path = join(dir, file)
                if match(rx, path):
                    self.files.append(path)

def find_tests(rx):
    finder = TestFileFinder()
    os.path.walk(join('lib','python'), finder.visit, rx)
    return finder.files

def package_import(modname):
    mod = __import__(modname)
    for part in modname.split(".")[1:]:
        mod = getattr(mod, part)
    return mod

def module_from_path(path):
    """Return the Python package name indiciated by the filesystem path."""

    assert path.endswith('.py')
    path = path[:-3]
    dirs = []
    while path:
        path, end = os.path.split(path)
        dirs.insert(0, end)
    return ".".join(dirs[2:])

def get_suite(file):
    modname = module_from_path(file)
    try:
        mod = package_import(modname)
    except ImportError, err:
        # print traceback
        print "Error importing %s\n%s" % (modname, err)
        print_tb_last()
        print
        if debug:
            raise
        return None
    try:
        suite_func = mod.test_suite
    except AttributeError:
        print "No test_suite() in %s" % file
        return None
    return suite_func()

def filter_testcases(s, rx):
    new = unittest.TestSuite()
    for test in s._tests:
        if isinstance(test, unittest.TestCase):
            name = test.id() # Full test name: package.module.class.method
            name = name[1 + name.rfind('.'):] # extract method name
            if match(rx, name):
                new.addTest(test)
        else:
            filtered = filter_testcases(test, rx)
            if filtered:
                new.addTest(filtered)
    return new

def gui_runner(files, test_filter):
    sys.path.insert(0, join(os.getcwd(), 'utilities'))
    import unittestgui
    suites = []
    for file in files:
        suites.append(module_from_path(file) + '.test_suite')

    suites = ", ".join(suites)
    minimal = (GUI == 'minimal')
    unittestgui.main(suites, minimal)

def runner(files, test_filter, debug):
    runner = ImmediateTestRunner(verbosity=VERBOSE, debug=debug)
    suite = unittest.TestSuite()
    for file in files:
        s = get_suite(file)
        if test_filter is not None:
            s = filter_testcases(s, test_filter)

        if s is None:
            print 'Got "None" test in %s' % file
        else:
            suite.addTest(s)

    r = runner.run(suite)

def remove_stale_bytecode(arg, dirname, names):
    names = map(os.path.normcase, names)
    for name in names:
        if name.endswith(".pyc") or name.endswith(".pyo"):
            srcname = name[:-1]
            if srcname not in names:
                fullname = os.path.join(dirname, name)
                print "Removing stale bytecode file", fullname
                os.unlink(fullname)

def main(module_filter, test_filter):
    os.path.walk(os.curdir, remove_stale_bytecode, None)
    setup_path()
    files = find_tests(module_filter)
    files.sort()

    if GUI:
        gui_runner(files, test_filter)
    elif LOOP:
        while 1:
            runner(files, test_filter, debug)
    else:
        runner(files, test_filter, debug)


def process_args():
    import getopt
    global module_filter
    global test_filter
    global VERBOSE
    global LOOP
    global GUI
    global debug
    global build
    global gcthresh

    module_filter = None
    test_filter = None
    VERBOSE = 0
    LOOP = 0
    GUI = 0
    debug = 0 # Don't collect test results; simply let tests crash
    build = 0
    gcthresh = None
    gcflags = []

    try:
        opts, args = getopt.getopt(sys.argv[1:], 'vdLbhCumg:G:',
                                   ['help'])
    except getopt.error, msg:
        print msg
        print "Try `python %s -h' for more information." % sys.argv[0]
        sys.exit(2)

    for k, v in opts:
        if k == '-v':
            VERBOSE += 1
        elif k == '-d':
            debug = 1
        elif k == '-L':
            LOOP = 1
        elif k == '-b':
            build = 1
        elif k in ('-h', '--help'):
            print __doc__
            sys.exit(0)
        elif k == '-C':
            import pychecker.checker
        elif k == '-g':
            gcthresh = int(v)
        elif k == '-u':
            GUI = 1
        elif k == '-m':
            GUI = 'minimal'
        elif k == '-G':
            if not v.startswith("DEBUG_"):
                print "-G argument must be DEBUG_ flag, not", repr(v)
                sys.exit(1)
            gcflags.append(v)

    if gcthresh is not None:
        import gc
        gc.set_threshold(gcthresh)
        print 'gc threshold:', gc.get_threshold()

    if gcflags:
        import gc
        val = 0
        for flag in gcflags:
            v = getattr(gc, flag, None)
            if v is None:
                print "Unknown gc flag", repr(flag)
                print gc.set_debug.__doc__
                sys.exit(1)
            val |= v
        gc.set_debug(v)

    if build:
        cmd = sys.executable + " setup.py -q build_ext -i"
        if VERBOSE:
            print cmd
        sts = os.system(cmd)
        if sts:
            print "Build failed", hex(sts)
            sys.exit(1)

    if args:
        if len(args) > 1:
            test_filter = args[1]
        module_filter = args[0]
    try:
        bad = main(module_filter, test_filter)
        if bad:
            sys.exit(1)
    except ImportError, err:
        print err
        print sys.path
        raise



def print_tb_last():
    """Print up to 'limit' stack trace entries from the traceback 'tb'.

    If 'limit' is omitted or None, all entries are printed.  If 'file'
    is omitted or None, the output goes to sys.stderr; otherwise
    'file' should be an open file or file-like object with a write()
    method.
    """
    tb = sys.exc_info()[2]
    file = sys.stderr
    while 1:
        f = tb.tb_frame
        lineno = traceback.tb_lineno(tb)
        tb = tb.tb_next
        if tb is not None:
            continue

        co = f.f_code
        filename = co.co_filename
        name = co.co_name
        file.write('  File "%s", line %d, in %s\n' % (filename,lineno,name))
        line = linecache.getline(filename, lineno)
        if line: file.write('    %s\n' % line.strip())
        break

if __name__ == "__main__":
    process_args()



=== Added File Zope27/wo_pcgi.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################
"""Try to do all of the installation steps.

This must be run from the top-level directory of the installation.
\(Yes, this is cheezy.  We'll fix this when we have a chance.)

"""

import sys, os

def setup(me):
    home=os.path.split(me)[0]
    if not home or home=='.': home=os.getcwd()
    sys.path.insert(0, os.path.join(home,'inst'))
    return home

def main(me):
    home=setup(me)
    import walkandscrub
    walkandscrub.walkandscrub(home)
    import compilezpy
    import build_extensions
    user=group=''
    import default_content; default_content.main(home, user, group)
    
    pcgi=os.path.join(home, 'Zope.cgi')
    import make_start; make_start.sh(home, user, group)
    import zpasswd; zpasswd.write_inituser(home, user, group)

    print '-'*78
    print
    print 'Done!'

if __name__=='__main__': main(sys.argv[0])


=== Added File Zope27/z2.py === (737/837 lines abridged)
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################
"""Zope 2 ZServer start-up file

Usage: %(program)s [options] [environment settings]

Options:

  -h

    Output this text.

  -z path

    The location of the Zope installation.
    The default is the location of this script, %(here)s.

  -Z path

    Unix only! This option is ignored on windows.

    If this option is specified, a separate managemnt process will
    be created that restarts Zope after a shutdown (or crash).
    The path must point to a pid file that the process will record its
    process id in. The path may be relative, in which case it will be
    relative to the Zope location.

    To prevent use of a separate management process, provide an
    empty string: -Z=''

  -t n

    The number of threads to use, if ZODB3 is used. The default is
    %(NUMBER_OF_THREADS)s.

  -i n

    Set the interpreter check interval. This integer value
    determines how often the interpreter checks for periodic things
    such as thread switches and signal handlers. The Zope default

[-=- -=- -=- 737 lines omitted -=- -=- -=-]

                    gid = pwd.getpwnam(UID)[3]
                elif isinstance(UID, IntType):
                    uid = pwd.getpwuid(UID)[2]
                    gid = pwd.getpwuid(UID)[3]
                    UID = pwd.getpwuid(UID)[0]
                else:
                    raise KeyError 
                if UID == 'nobody':
                    _warn_nobody()
                try:
                    initgroups.initgroups(UID, gid)
                    if gid is not None:
                        try:
                            os.setgid(gid)
                        except OSError:
                            pass
                    os.setuid(uid)
                except OSError:
                    pass
            except KeyError:
                zLOG.LOG("z2", zLOG.ERROR, ("Can't find UID %s" % UID))
    except AttributeError:
        pass
    except:
        raise

    # Check umask sanity if we're on posix.
    if os.name == 'posix':
        # umask is silly, blame POSIX.  We have to set it to get its value.
        current_umask = os.umask(0)
        os.umask(current_umask)
        if current_umask != 077: 
            current_umask = '%03o' % current_umask
            zLOG.LOG("z2", zLOG.INFO, (
                'Your umask of %s may be too permissive; for the security of '
                'your Zope data, it is recommended you use 077' % current_umask
                ))

except:
    # Log startup exception and tell zdaemon not to restart us.
    try:
        zLOG.LOG("z2", zLOG.PANIC, "Startup exception",
                 error=sys.exc_info())
    except: pass
    sys.exit(0)

# Start Medusa, Ye Hass!
sys.ZServerExitCode=0
asyncore.loop()
sys.exit(sys.ZServerExitCode)


=== Added File Zope27/zpasswd.py ===
#!/usr/bin/env python
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################
"""Zope user bootstrap system"""

__version__='$Revision: 1.1.2.1 $ '[11:-2]

import sys,  sha, binascii, random, getopt, getpass, os

try:
    from crypt import crypt
except ImportError:
    crypt = None

def generate_salt():
    """Generate a salt value for the crypt function."""
    salt_choices = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                    "abcdefghijklmnopqrstuvwxyz"
                    "0123456789./")
    return random.choice(salt_choices)+random.choice(salt_choices)

def generate_passwd(password, encoding):
    encoding=encoding.upper()
    if encoding == 'SHA':
        pw = '{SHA}' + binascii.b2a_base64(sha.new(password).digest())[:-1]
    elif encoding == 'CRYPT':
        pw = '{CRYPT}' + crypt(password, generate_salt())
    elif encoding == 'CLEARTEXT':
        pw = password

    return pw

def write_generated_password(home, ac_path, username):
    pw_choices = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                  "abcdefghijklmnopqrstuvwxyz"
                  "0123456789!")
    acfile=open(ac_path, 'w')
    pw = ''
    for i in range(8):
        pw = pw + random.choice(pw_choices)
    acfile.write('%s:%s' % (username, generate_passwd(pw, 'SHA')))
    acfile.close()
    os.system('chmod 644 %s' % ac_path)
    return pw
    
def write_access(home, user='', group=''):
    ac_path=os.path.join(home, 'access')
    if not os.path.exists(ac_path):
        print '-'*78
        print 'creating default access file'
        pw = write_generated_password(home, ac_path, 'emergency')
        print """Note:
        The emergency user name and password are 'emergency'
        and '%s'.

        You can change the emergency name and password with the
        zpasswd script.  To find out more, type:

        %s zpasswd.py
        """ % (pw, sys.executable)

        import do; do.ch(ac_path, user, group)

def write_inituser(home, user='', group=''):
    ac_path=os.path.join(home, 'inituser')
    if not os.path.exists(ac_path):
        print '-'*78
        print 'creating default inituser file'
        pw = write_generated_password(home, ac_path, 'admin')
        print """Note:
        The initial user name and password are 'admin'
        and '%s'.

        You can change the name and password through the web
        interface or using the 'zpasswd.py' script.
        """ % pw

        import do; do.ch(ac_path, user, group)


def main(argv):
    short_options = ':u:p:e:d:'
    long_options = ['username=',
                    'password=',
                    'encoding=',
                    'domains=']

    usage = """Usage: %s [options] filename
If this program is called without command-line options, it will prompt
for all necessary information.  The available options are:

    -u / --username=
    Set the username to be used for the initial user or the emergency user

    -p / --password=
    Set the password

    -e / --encoding=
    Set the encryption/encoding rules.  Defaults to SHA-1. OPTIONAL

    -d / --domains=
    Set the domain names that the user user can log in from.  Defaults to
    any. OPTIONAL.
    
    Filename is required and should be the name of the file to store the
    information in (usually "inituser" or "access").
    
Copyright (C) 1999, 2000 Digital Creations, Inc.
""" % argv[0]

    try:
        if len(argv) < 2:
            raise "CommandLineError"
        
        optlist, args = getopt.getopt(sys.argv[1:], short_options, long_options)

        if len(args) != 1:
            raise "CommandLineError"

        access_file = open(args[0], 'w')

        if len(optlist) > 0:
            # Set the sane defaults
            username = ''
            encoding = 'SHA'
            domains = ''
        
            for opt in optlist:
                if (opt[0] == '-u') or (opt[0] == '--username'):
                    username = opt[1]
                elif (opt[0] == '-p') or (opt[0] == '--password'):
                    password = opt[1]
                elif (opt[0] == '-e') or (opt[0] == '--encoding'):
                    encoding = opt[1]
                elif (opt[0] == '-d') or (opt[0] == '--domains'):
                    domains = ":" + opt[1]

            # Verify that we got what we need
            if not username or not password:
                raise "CommandLineError"

            access_file.write(username + ':' +
                              generate_passwd(password, encoding) +
                              domains)

        else:
            # Run through the prompts
            while 1:
                username = raw_input("Username: ")
                if username != '':
                    break
               
            while 1:
                password = getpass.getpass("Password: ")
                verify = getpass.getpass("Verify password: ")
                if verify == password:
                    break
                else:
                    password = verify = ''
                    print "Password mismatch, please try again..."

            while 1:
                print """
Please choose a format from:

SHA - SHA-1 hashed password (default)
CRYPT - UNIX-style crypt password
CLEARTEXT - no protection
"""
                encoding = raw_input("Encoding: ")
                if encoding == '':
                    encoding = 'SHA'
                    break
                if encoding.upper() in ['SHA', 'CRYPT', 'CLEARTEXT']:
                    break

            domains = raw_input("Domain restrictions: ")
            if domains: domains = ":" + domains

            access_file.write(username + ":" +
                              generate_passwd(password, encoding) +
                              domains)
            
    except "CommandLineError":
        sys.stderr.write(usage)
        sys.exit(1)

    
# If called from the command line
if __name__=='__main__': main(sys.argv)